diff --git a/DEPS b/DEPS
index 8344f20..b2a2fcf 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '43a5497bf64323031753fd40432c7f0b2de5cd50',
+  'skia_revision': '9c37cd96e4c8158643dbde6fd3eda1b42e267bec',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '80d8889e4e4eead687e2aabe7dc60c23c17bd3a1',
+  'v8_revision': 'f70aaa8ab2e8815505a6145c745e50d8328cd28c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -137,7 +137,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'ce00828c89df3d4c40de7d715b1a032eb03c525c',
+  'boringssl_revision': '2d98d49cf712ca7dc6f4b23b9c5f5542385d8dbe',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -165,7 +165,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': 'cd3378c32017c99062cd467a3ff601e0b57f0c23',
+  'catapult_revision': 'c8b97e37ec9c447eee46830c6e0598af7b9bd539',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -600,7 +600,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'abdecf34eac97c4f2b2d2e5b2d6bea5d4b97fee1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7b5fdb9c2a7f35145dda05caf3bef1de8929c50b',
       'condition': 'checkout_linux',
   },
 
@@ -1010,7 +1010,7 @@
   },
 
   'src/third_party/re2/src':
-    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '54ca2cd59219aab637e7a5e1e6d0f383a36df192',
+    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '6272edcb53d3c8705f57df3b46b1f92e646e30bc',
 
   'src/third_party/r8': {
       'packages': [
@@ -1106,7 +1106,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e8d2b1be1a7c39fd405a9f8061467008869f24bd',
+    Var('webrtc_git') + '/src.git' + '@' + 'f81170b48fd43fa4463d5cddd2815aaae6f30217',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1137,7 +1137,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fbc78231d0587866b6ee16516308e11ec04823e7',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2146d0609ce9176edd452ec0bcbb225a4aca43c6',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 9e2b76b..8ee3606 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -618,7 +618,7 @@
                   '|chrome/browser/resources/settings/',
     },
     'browsing_data': {
-      'filepath': '/browsing_data/',
+      'filepath': 'browsing_data|BrowsingData',
     },
     'bubble': {
       'filepath': 'ui/views/bubble/|'\
diff --git a/ash/system/message_center/message_list_view.cc b/ash/system/message_center/message_list_view.cc
index 698c202..6e2b5a2 100644
--- a/ash/system/message_center/message_list_view.cc
+++ b/ash/system/message_center/message_list_view.cc
@@ -433,7 +433,7 @@
   if (need_update)
     DoUpdateIfPossible();
 
-  if (GetWidget())
+  if (GetWidget() && !GetWidget()->IsClosed())
     GetWidget()->SynthesizeMouseMoveEvent();
 }
 
diff --git a/ash/system/message_center/new_unified_message_center_view.cc b/ash/system/message_center/new_unified_message_center_view.cc
index bb99c34..7a03cc0 100644
--- a/ash/system/message_center/new_unified_message_center_view.cc
+++ b/ash/system/message_center/new_unified_message_center_view.cc
@@ -96,7 +96,7 @@
   PreferredSizeChanged();
   ScrollToPositionFromBottom();
 
-  if (GetWidget())
+  if (GetWidget() && !GetWidget()->IsClosed())
     GetWidget()->SynthesizeMouseMoveEvent();
 }
 
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 293bec3..6e4019b 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -44,6 +44,7 @@
 #include "ui/compositor_extra/shadow.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/shadow_controller.h"
 #include "ui/wm/core/shadow_types.h"
@@ -89,6 +90,17 @@
   DISALLOW_COPY_AND_ASSIGN(OverviewStatesObserver);
 };
 
+// The test BubbleDialogDelegateView for bubbles.
+class TestBubbleDialogDelegateView : public views::BubbleDialogDelegateView {
+ public:
+  explicit TestBubbleDialogDelegateView(views::View* anchor_view)
+      : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) {}
+  ~TestBubbleDialogDelegateView() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
+};
+
 }  // namespace
 
 class SplitViewControllerTest : public AshTestBase {
@@ -1614,6 +1626,34 @@
   EXPECT_FALSE(Shell::Get()->window_selector_controller()->IsSelecting());
 }
 
+// Tests that if a snapped window has a bubble transient child, the bubble's
+// bounds should always align with the snapped window's bounds.
+TEST_F(SplitViewControllerTest, AdjustTransientChildBounds) {
+  std::unique_ptr<views::Widget> widget(CreateTestWidget());
+  aura::Window* window = widget->GetNativeWindow();
+  window->SetProperty(aura::client::kResizeBehaviorKey,
+                      ws::mojom::kResizeBehaviorCanResize |
+                          ws::mojom::kResizeBehaviorCanMaximize);
+  split_view_controller()->SnapWindow(window, SplitViewController::LEFT);
+  const gfx::Rect window_bounds = window->GetBoundsInScreen();
+
+  // Create a bubble widget that's anchored to |widget|.
+  views::Widget* bubble_widget = views::BubbleDialogDelegateView::CreateBubble(
+      new TestBubbleDialogDelegateView(widget->GetContentsView()));
+  aura::Window* bubble_window = bubble_widget->GetNativeWindow();
+  EXPECT_TRUE(::wm::HasTransientAncestor(bubble_window, window));
+  // Test that the bubble is created inside its anchor widget.
+  EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen()));
+
+  // Now try to manually move the bubble out of the snapped window.
+  bubble_window->SetBoundsInScreen(
+      split_view_controller()->GetSnappedWindowBoundsInScreen(
+          window, SplitViewController::RIGHT),
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window));
+  // Test that the bubble can't be moved outside of its anchor widget.
+  EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen()));
+}
+
 // Test the tab-dragging related functionalities in tablet mode. Tab(s) can be
 // dragged out of a window and then put in split view mode or merge into another
 // window.
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index 8b957b2..2f80bdf 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -25,7 +25,10 @@
 #include "ui/views/view.h"
 #include "ui/views/view_targeter_delegate.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
 #include "ui/wm/core/coordinate_conversion.h"
+#include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
 namespace ash {
@@ -383,6 +386,7 @@
 void SplitViewDivider::AddObservedWindow(aura::Window* window) {
   if (!base::ContainsValue(observed_windows_, window)) {
     window->AddObserver(this);
+    ::wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
     observed_windows_.push_back(window);
   }
 }
@@ -392,6 +396,7 @@
       std::find(observed_windows_.begin(), observed_windows_.end(), window);
   if (iter != observed_windows_.end()) {
     window->RemoveObserver(this);
+    ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
     observed_windows_.erase(iter);
   }
 }
@@ -413,6 +418,33 @@
   RemoveObservedWindow(window);
 }
 
+void SplitViewDivider::OnWindowBoundsChanged(aura::Window* window,
+                                             const gfx::Rect& old_bounds,
+                                             const gfx::Rect& new_bounds,
+                                             ui::PropertyChangeReason reason) {
+  // We only care about the bounds change of windows in
+  // |transient_windows_observer_|.
+  if (!transient_windows_observer_.IsObserving(window))
+    return;
+
+  // |window|'s transient parent must be one of the windows in
+  // |observed_windows_|.
+  aura::Window* transient_parent = nullptr;
+  for (auto* observed_window : observed_windows_) {
+    if (::wm::HasTransientAncestor(window, observed_window)) {
+      transient_parent = observed_window;
+      break;
+    }
+  }
+  DCHECK(transient_parent);
+
+  gfx::Rect transient_bounds = window->GetBoundsInScreen();
+  transient_bounds.AdjustToFit(transient_parent->GetBoundsInScreen());
+  window->SetBoundsInScreen(
+      transient_bounds,
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window));
+}
+
 void SplitViewDivider::OnWindowActivated(ActivationReason reason,
                                          aura::Window* gained_active,
                                          aura::Window* lost_active) {
@@ -428,6 +460,26 @@
   }
 }
 
+void SplitViewDivider::OnTransientChildAdded(aura::Window* window,
+                                             aura::Window* transient) {
+  // For now, we only care about dialog bubbles type transient child. We may
+  // observe other types transient child window as well if need arises in the
+  // future.
+  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(transient);
+  if (!widget || !widget->widget_delegate()->AsBubbleDialogDelegate())
+    return;
+
+  // At this moment, the transient window may not have the valid bounds yet.
+  // Start observe the transient window.
+  transient_windows_observer_.Add(transient);
+}
+
+void SplitViewDivider::OnTransientChildRemoved(aura::Window* window,
+                                               aura::Window* transient) {
+  if (transient_windows_observer_.IsObserving(transient))
+    transient_windows_observer_.Remove(transient);
+}
+
 void SplitViewDivider::CreateDividerWidget(aura::Window* root_window) {
   DCHECK(!divider_widget_);
   // Native widget owns this widget.
diff --git a/ash/wm/splitview/split_view_divider.h b/ash/wm/splitview/split_view_divider.h
index aa9349dd..25a8f3e 100644
--- a/ash/wm/splitview/split_view_divider.h
+++ b/ash/wm/splitview/split_view_divider.h
@@ -9,12 +9,14 @@
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
+#include "base/scoped_observer.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/wm/core/transient_window_observer.h"
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace views {
@@ -34,7 +36,8 @@
 // to resize the left and right windows accordingly. The divider widget should
 // always placed above its observed windows to be able to receive events.
 class ASH_EXPORT SplitViewDivider : public aura::WindowObserver,
-                                    public ::wm::ActivationChangeObserver {
+                                    public ::wm::ActivationChangeObserver,
+                                    public ::wm::TransientWindowObserver {
  public:
   SplitViewDivider(SplitViewController* controller, aura::Window* root_window);
   ~SplitViewDivider() override;
@@ -74,6 +77,16 @@
   void OnWindowActivated(ActivationReason reason,
                          aura::Window* gained_active,
                          aura::Window* lost_active) override;
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+
+  // ::wm::TransientWindowObserver:
+  void OnTransientChildAdded(aura::Window* window,
+                             aura::Window* transient) override;
+  void OnTransientChildRemoved(aura::Window* window,
+                               aura::Window* transient) override;
 
   views::Widget* divider_widget() { return divider_widget_; }
 
@@ -101,6 +114,10 @@
   // Tracks observed windows.
   aura::Window::Windows observed_windows_;
 
+  // Tracks observed transient windows.
+  ScopedObserver<aura::Window, aura::WindowObserver>
+      transient_windows_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(SplitViewDivider);
 };
 
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index 70177cd..197bdeb 100644
--- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -46,6 +46,7 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.textclassifier.TextClassifier;
 import android.widget.ImageView;
+import android.widget.PopupWindow;
 import android.widget.TextView;
 
 import java.io.File;
@@ -326,6 +327,17 @@
     }
 
     /**
+     * Set elevation if supported.
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static boolean setElevation(PopupWindow window, float elevationValue) {
+        if (!isElevationSupported()) return false;
+
+        window.setElevation(elevationValue);
+        return true;
+    }
+
+    /**
      *  Gets an intent to start the Android system notification settings activity for an app.
      *
      *  @param context Context of the app whose settings intent should be returned.
diff --git a/base/android/scoped_java_ref.h b/base/android/scoped_java_ref.h
index 8bb18d4..5e8bc04 100644
--- a/base/android/scoped_java_ref.h
+++ b/base/android/scoped_java_ref.h
@@ -36,42 +36,51 @@
 };
 
 // Forward declare the generic java reference template class.
-template<typename T> class JavaRef;
+template <typename T>
+class JavaRef;
 
 // Template specialization of JavaRef, which acts as the base class for all
 // other JavaRef<> template types. This allows you to e.g. pass
 // ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
-template<>
+template <>
 class BASE_EXPORT JavaRef<jobject> {
  public:
-  // Initializes a null reference. Don't add anything else here; it's inlined.
-  constexpr JavaRef() : obj_(nullptr) {}
+  // Initializes a null reference.
+  constexpr JavaRef() {}
 
   // Allow nullptr to be converted to JavaRef. This avoids having to declare an
   // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and
   // Java "null" equivalent.
-  constexpr JavaRef(std::nullptr_t) : JavaRef() {}
+  constexpr JavaRef(std::nullptr_t) {}
 
   // Public to allow destruction of null JavaRef objects.
-  // Don't add anything else here; it's inlined.
   ~JavaRef() {}
 
+  // TODO(torne): maybe rename this to get() for consistency with unique_ptr
+  // once there's fewer unnecessary uses of it in the codebase.
   jobject obj() const { return obj_; }
 
+  explicit operator bool() const { return obj_ != nullptr; }
+
+  // Deprecated. Just use bool conversion.
+  // TODO(torne): replace usage and remove this.
   bool is_null() const { return obj_ == nullptr; }
 
  protected:
-  // Takes ownership of the |obj| reference passed; requires it to be a local
-  // reference type.
+// Takes ownership of the |obj| reference passed; requires it to be a local
+// reference type.
 #if DCHECK_IS_ON()
   // Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON.
   JavaRef(JNIEnv* env, jobject obj);
 #else
-  // Don't add anything else here; it's inlined.
   JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {}
 #endif
 
-  void swap(JavaRef& other) { std::swap(obj_, other.obj_); }
+  // Used for move semantics. obj_ must have been released first if non-null.
+  void steal(JavaRef&& other) {
+    obj_ = other.obj_;
+    other.obj_ = nullptr;
+  }
 
   // The following are implementation detail convenience methods, for
   // use by the sub-classes.
@@ -82,7 +91,7 @@
   jobject ReleaseInternal();
 
  private:
-  jobject obj_;
+  jobject obj_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(JavaRef);
 };
@@ -90,11 +99,11 @@
 // Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
 // for allowing functions to accept a reference without having to mandate
 // whether it is a local or global type.
-template<typename T>
+template <typename T>
 class JavaRef : public JavaRef<jobject> {
  public:
-  JavaRef() {}
-  JavaRef(std::nullptr_t) : JavaRef<jobject>(nullptr) {}
+  constexpr JavaRef() {}
+  constexpr JavaRef(std::nullptr_t) {}
   ~JavaRef() {}
 
   T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
@@ -110,7 +119,7 @@
 // Method parameters should not be deleted, and so this class exists purely to
 // wrap them as a JavaRef<T> in the JNI binding generator. Do not create
 // instances manually.
-template<typename T>
+template <typename T>
 class JavaParamRef : public JavaRef<T> {
  public:
   // Assumes that |obj| is a parameter passed to a JNI method from Java.
@@ -121,7 +130,7 @@
   // methods directly from C++ and pass null for objects which are not actually
   // used by the implementation (e.g. the caller object); allow this to keep
   // working.
-  JavaParamRef(std::nullptr_t) : JavaRef<T>(nullptr) {}
+  JavaParamRef(std::nullptr_t) {}
 
   ~JavaParamRef() {}
 
@@ -143,81 +152,126 @@
 // single thread. If you wish to have the reference outlive the current
 // callstack (e.g. as a class member) or you wish to pass it across threads,
 // use a ScopedJavaGlobalRef instead.
-template<typename T>
+template <typename T>
 class ScopedJavaLocalRef : public JavaRef<T> {
  public:
-  constexpr ScopedJavaLocalRef() : env_(nullptr) {}
-  constexpr ScopedJavaLocalRef(std::nullptr_t) : env_(nullptr) {}
-
-  // Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned
-  // by value as this is the normal usage pattern.
-  ScopedJavaLocalRef(const ScopedJavaLocalRef<T>& other)
-      : env_(other.env_) {
-    this->SetNewLocalRef(env_, other.obj());
+  // Take ownership of a bare jobject. This does not create a new reference.
+  // This should only be used by JNI helper functions, or in cases where code
+  // must call JNIEnv methods directly.
+  static ScopedJavaLocalRef Adopt(JNIEnv* env, T obj) {
+    return ScopedJavaLocalRef(env, obj);
   }
 
-  ScopedJavaLocalRef(ScopedJavaLocalRef<T>&& other) : env_(other.env_) {
-    this->swap(other);
+  constexpr ScopedJavaLocalRef() {}
+  constexpr ScopedJavaLocalRef(std::nullptr_t) {}
+
+  // Copy constructor. This is required in addition to the copy conversion
+  // constructor below.
+  ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : env_(other.env_) {
+    JavaRef<T>::SetNewLocalRef(env_, other.obj());
   }
 
-  explicit ScopedJavaLocalRef(const JavaRef<T>& other) : env_(nullptr) {
-    this->Reset(other);
+  // Copy conversion constructor.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaLocalRef(const ScopedJavaLocalRef<U>& other) : env_(other.env_) {
+    JavaRef<T>::SetNewLocalRef(env_, other.obj());
   }
 
+  // Move constructor. This is required in addition to the move conversion
+  // constructor below.
+  ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) {
+    JavaRef<T>::steal(std::move(other));
+  }
+
+  // Move conversion constructor.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaLocalRef(ScopedJavaLocalRef<U>&& other) : env_(other.env_) {
+    JavaRef<T>::steal(std::move(other));
+  }
+
+  // Constructor for other JavaRef types.
+  explicit ScopedJavaLocalRef(const JavaRef<T>& other) { Reset(other); }
+
   // Assumes that |obj| is a local reference to a Java object and takes
-  // ownership  of this local reference.
-  // TODO(torne): this shouldn't be used outside of JNI helper functions but
-  // there are currently some cases where there aren't helpers for things.
+  // ownership of this local reference.
+  // TODO(torne): make legitimate uses call Adopt() instead, and make this
+  // private.
   ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
 
-  ~ScopedJavaLocalRef() {
-    this->Reset();
+  ~ScopedJavaLocalRef() { Reset(); }
+
+  // Null assignment, for disambiguation.
+  ScopedJavaLocalRef& operator=(std::nullptr_t) {
+    Reset();
+    return *this;
   }
 
-  // Overloaded assignment operator defined for consistency with the implicit
-  // copy constructor.
-  void operator=(const ScopedJavaLocalRef<T>& other) {
-    this->Reset(other);
+  // Copy assignment.
+  ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef& other) {
+    Reset(other);
+    return *this;
   }
 
-  void operator=(ScopedJavaLocalRef<T>&& other) {
+  // Copy conversion assignment.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef<U>& other) {
+    Reset(other);
+    return *this;
+  }
+
+  // Move assignment.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaLocalRef& operator=(ScopedJavaLocalRef<U>&& other) {
     env_ = other.env_;
-    this->swap(other);
+    Reset();
+    JavaRef<T>::steal(std::move(other));
+    return *this;
   }
 
-  void Reset() {
-    this->ResetLocalRef(env_);
+  // Assignment for other JavaRef types.
+  ScopedJavaLocalRef& operator=(const JavaRef<T>& other) {
+    Reset(other);
+    return *this;
   }
 
-  void Reset(const ScopedJavaLocalRef<T>& other) {
+  void Reset() { JavaRef<T>::ResetLocalRef(env_); }
+
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  void Reset(const ScopedJavaLocalRef<U>& other) {
     // We can copy over env_ here as |other| instance must be from the same
     // thread as |this| local ref. (See class comment for multi-threading
     // limitations, and alternatives).
-    this->Reset(other.env_, other.obj());
+    Reset(other.env_, other.obj());
   }
 
   void Reset(const JavaRef<T>& other) {
     // If |env_| was not yet set (is still null) it will be attached to the
     // current thread in SetNewLocalRef().
-    this->Reset(env_, other.obj());
+    Reset(env_, other.obj());
   }
 
   // Creates a new local reference to the Java object, unlike the constructor
   // with the same parameters that takes ownership of the existing reference.
-  // TODO(torne): these should match as this is confusing.
-  void Reset(JNIEnv* env, T obj) { env_ = this->SetNewLocalRef(env, obj); }
+  // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
+  // TODO(torne): fix existing usage and remove this.
+  void Reset(JNIEnv* env, T obj) {
+    env_ = JavaRef<T>::SetNewLocalRef(env, obj);
+  }
 
   // Releases the local reference to the caller. The caller *must* delete the
   // local reference when it is done with it. Note that calling a Java method
   // is *not* a transfer of ownership and Release() should not be used.
-  T Release() {
-    return static_cast<T>(this->ReleaseInternal());
-  }
+  T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
 
  private:
   // This class is only good for use on the thread it was created on so
   // it's safe to cache the non-threadsafe JNIEnv* inside this object.
-  JNIEnv* env_;
+  JNIEnv* env_ = nullptr;
 
   // Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take
   // ownership of a JavaParamRef's underlying object - parameters are not
@@ -225,58 +279,112 @@
   // TODO(torne): this can be removed once JavaParamRef no longer has an
   // implicit conversion back to T.
   ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef<T>& other);
+
+  // Friend required to get env_ from conversions.
+  template <typename U>
+  friend class ScopedJavaLocalRef;
 };
 
 // Holds a global reference to a Java object. The global reference is scoped
 // to the lifetime of this object. This class does not hold onto any JNIEnv*
 // passed to it, hence it is safe to use across threads (within the constraints
 // imposed by the underlying Java object that it references).
-template<typename T>
+template <typename T>
 class ScopedJavaGlobalRef : public JavaRef<T> {
  public:
   constexpr ScopedJavaGlobalRef() {}
   constexpr ScopedJavaGlobalRef(std::nullptr_t) {}
 
-  ScopedJavaGlobalRef(const ScopedJavaGlobalRef<T>& other) {
-    this->Reset(other);
+  // Copy constructor. This is required in addition to the copy conversion
+  // constructor below.
+  ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { Reset(other); }
+
+  // Copy conversion constructor.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U>& other) {
+    Reset(other);
   }
 
-  ScopedJavaGlobalRef(ScopedJavaGlobalRef<T>&& other) { this->swap(other); }
-
-  ScopedJavaGlobalRef(JNIEnv* env, T obj) { this->Reset(env, obj); }
-
-  explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { this->Reset(other); }
-
-  ~ScopedJavaGlobalRef() {
-    this->Reset();
+  // Move constructor. This is required in addition to the move conversion
+  // constructor below.
+  ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) {
+    JavaRef<T>::steal(std::move(other));
   }
 
-  // Overloaded assignment operator defined for consistency with the implicit
-  // copy constructor.
-  void operator=(const ScopedJavaGlobalRef<T>& other) {
-    this->Reset(other);
+  // Move conversion constructor.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaGlobalRef(ScopedJavaGlobalRef<U>&& other) {
+    JavaRef<T>::steal(std::move(other));
   }
 
-  void operator=(ScopedJavaGlobalRef<T>&& other) { this->swap(other); }
+  // Conversion constructor for other JavaRef types.
+  explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { Reset(other); }
 
-  void Reset() {
-    this->ResetGlobalRef();
+  // Create a new global reference to the object.
+  // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
+  ScopedJavaGlobalRef(JNIEnv* env, T obj) { Reset(env, obj); }
+
+  ~ScopedJavaGlobalRef() { Reset(); }
+
+  // Null assignment, for disambiguation.
+  ScopedJavaGlobalRef& operator=(std::nullptr_t) {
+    Reset();
+    return *this;
   }
 
-  void Reset(const JavaRef<T>& other) { this->Reset(nullptr, other.obj()); }
+  // Copy assignment.
+  ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef& other) {
+    Reset(other);
+    return *this;
+  }
 
+  // Copy conversion assignment.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef<U>& other) {
+    Reset(other);
+    return *this;
+  }
+
+  // Move assignment.
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  ScopedJavaGlobalRef& operator=(ScopedJavaGlobalRef<U>&& other) {
+    Reset();
+    JavaRef<T>::steal(std::move(other));
+    return *this;
+  }
+
+  // Assignment for other JavaRef types.
+  ScopedJavaGlobalRef& operator=(const JavaRef<T>& other) {
+    Reset(other);
+    return *this;
+  }
+
+  void Reset() { JavaRef<T>::ResetGlobalRef(); }
+
+  template <typename U,
+            typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+  void Reset(const ScopedJavaGlobalRef<U>& other) {
+    Reset(nullptr, other.obj());
+  }
+
+  void Reset(const JavaRef<T>& other) { Reset(nullptr, other.obj()); }
+
+  // Deprecated. You can just use Reset(const JavaRef&).
   void Reset(JNIEnv* env, const JavaParamRef<T>& other) {
-    this->Reset(env, other.obj());
+    Reset(env, other.obj());
   }
 
-  void Reset(JNIEnv* env, T obj) { this->SetNewGlobalRef(env, obj); }
+  // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
+  void Reset(JNIEnv* env, T obj) { JavaRef<T>::SetNewGlobalRef(env, obj); }
 
   // Releases the global reference to the caller. The caller *must* delete the
   // global reference when it is done with it. Note that calling a Java method
   // is *not* a transfer of ownership and Release() should not be used.
-  T Release() {
-    return static_cast<T>(this->ReleaseInternal());
-  }
+  T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
 };
 
 }  // namespace android
diff --git a/base/android/scoped_java_ref_unittest.cc b/base/android/scoped_java_ref_unittest.cc
index 99d035b..d46c5ac 100644
--- a/base/android/scoped_java_ref_unittest.cc
+++ b/base/android/scoped_java_ref_unittest.cc
@@ -8,6 +8,9 @@
 #include "base/android/jni_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#define EXPECT_SAME_OBJECT(a, b) \
+  EXPECT_TRUE(env->IsSameObject(a.obj(), b.obj()))
+
 namespace base {
 namespace android {
 
@@ -69,13 +72,110 @@
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
   ScopedJavaGlobalRef<jstring> global(str);
+
+  // Contextual conversions to bool should be allowed.
+  EXPECT_TRUE(str);
+  EXPECT_FALSE(JavaRef<jobject>());
+
+  // All the types should convert from nullptr, even JavaRef.
+  {
+    JavaRef<jstring> null_ref(nullptr);
+    EXPECT_FALSE(null_ref);
+    ScopedJavaLocalRef<jobject> null_local(nullptr);
+    EXPECT_FALSE(null_local);
+    ScopedJavaGlobalRef<jarray> null_global(nullptr);
+    EXPECT_FALSE(null_global);
+  }
+
+  // Local and global refs should {copy,move}-{construct,assign}.
+  // Moves should leave the source as null.
+  {
+    ScopedJavaLocalRef<jstring> str2(str);
+    EXPECT_SAME_OBJECT(str2, str);
+    ScopedJavaLocalRef<jstring> str3(std::move(str2));
+    EXPECT_SAME_OBJECT(str3, str);
+    EXPECT_FALSE(str2);
+    ScopedJavaLocalRef<jstring> str4;
+    str4 = str;
+    EXPECT_SAME_OBJECT(str4, str);
+    ScopedJavaLocalRef<jstring> str5;
+    str5 = std::move(str4);
+    EXPECT_SAME_OBJECT(str5, str);
+    EXPECT_FALSE(str4);
+  }
+  {
+    ScopedJavaGlobalRef<jstring> str2(global);
+    EXPECT_SAME_OBJECT(str2, str);
+    ScopedJavaGlobalRef<jstring> str3(std::move(str2));
+    EXPECT_SAME_OBJECT(str3, str);
+    EXPECT_FALSE(str2);
+    ScopedJavaGlobalRef<jstring> str4;
+    str4 = global;
+    EXPECT_SAME_OBJECT(str4, str);
+    ScopedJavaGlobalRef<jstring> str5;
+    str5 = std::move(str4);
+    EXPECT_SAME_OBJECT(str5, str);
+    EXPECT_FALSE(str4);
+  }
+
+  // As above but going from jstring to jobject.
+  {
+    ScopedJavaLocalRef<jobject> obj2(str);
+    EXPECT_SAME_OBJECT(obj2, str);
+    ScopedJavaLocalRef<jobject> obj3(std::move(obj2));
+    EXPECT_SAME_OBJECT(obj3, str);
+    EXPECT_FALSE(obj2);
+    ScopedJavaLocalRef<jobject> obj4;
+    obj4 = str;
+    EXPECT_SAME_OBJECT(obj4, str);
+    ScopedJavaLocalRef<jobject> obj5;
+    obj5 = std::move(obj4);
+    EXPECT_SAME_OBJECT(obj5, str);
+    EXPECT_FALSE(obj4);
+  }
+  {
+    ScopedJavaGlobalRef<jobject> obj2(global);
+    EXPECT_SAME_OBJECT(obj2, str);
+    ScopedJavaGlobalRef<jobject> obj3(std::move(obj2));
+    EXPECT_SAME_OBJECT(obj3, str);
+    EXPECT_FALSE(obj2);
+    ScopedJavaGlobalRef<jobject> obj4;
+    obj4 = global;
+    EXPECT_SAME_OBJECT(obj4, str);
+    ScopedJavaGlobalRef<jobject> obj5;
+    obj5 = std::move(obj4);
+    EXPECT_SAME_OBJECT(obj5, str);
+    EXPECT_FALSE(obj4);
+  }
+
+  // Explicit copy construction or assignment between global<->local is allowed,
+  // but not implicit conversions.
+  {
+    ScopedJavaLocalRef<jstring> new_local(global);
+    EXPECT_SAME_OBJECT(new_local, str);
+    new_local = global;
+    EXPECT_SAME_OBJECT(new_local, str);
+    ScopedJavaGlobalRef<jstring> new_global(str);
+    EXPECT_SAME_OBJECT(new_global, str);
+    new_global = str;
+    EXPECT_SAME_OBJECT(new_local, str);
+    static_assert(!std::is_convertible<ScopedJavaLocalRef<jobject>,
+                                       ScopedJavaGlobalRef<jobject>>::value,
+                  "");
+    static_assert(!std::is_convertible<ScopedJavaGlobalRef<jobject>,
+                                       ScopedJavaLocalRef<jobject>>::value,
+                  "");
+  }
+
+  // Converting between local/global while also converting to jobject also works
+  // because JavaRef<jobject> is the base class.
   {
     ScopedJavaGlobalRef<jobject> global_obj(str);
     ScopedJavaLocalRef<jobject> local_obj(global);
     const JavaRef<jobject>& obj_ref1(str);
     const JavaRef<jobject>& obj_ref2(global);
-    EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj()));
-    EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj()));
+    EXPECT_SAME_OBJECT(obj_ref1, obj_ref2);
+    EXPECT_SAME_OBJECT(global_obj, obj_ref2);
   }
   global.Reset(str);
   const JavaRef<jstring>& str_ref = str;
@@ -99,7 +199,7 @@
     EXPECT_EQ(1, g_local_refs);
     EXPECT_EQ(2, g_global_refs);
 
-    ScopedJavaLocalRef<jstring> str2(env, str.Release());
+    auto str2 = ScopedJavaLocalRef<jstring>::Adopt(env, str.Release());
     EXPECT_EQ(1, g_local_refs);
     {
       ScopedJavaLocalRef<jstring> str3(str2);
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index 48b23b24..438e75a 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -1073,7 +1073,7 @@
   using MoveOnlyVector = std::vector<std::unique_ptr<int>>;
 
   MoveOnlyVector v;
-  v.push_back(WrapUnique(new int(12345)));
+  v.push_back(std::make_unique<int>(12345));
 
   // Early binding should work:
   base::Callback<MoveOnlyVector()> bound_cb =
diff --git a/base/cancelable_callback_unittest.cc b/base/cancelable_callback_unittest.cc
index 373498c..42e753f 100644
--- a/base/cancelable_callback_unittest.cc
+++ b/base/cancelable_callback_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/location.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -198,7 +197,7 @@
   int result = 0;
   CancelableCallback<void(std::unique_ptr<int>)> cb(
       base::Bind(&OnMoveOnlyReceived, base::Unretained(&result)));
-  cb.callback().Run(base::WrapUnique(new int(kExpectedResult)));
+  cb.callback().Run(std::make_unique<int>(kExpectedResult));
 
   EXPECT_EQ(kExpectedResult, result);
 }
diff --git a/base/containers/mru_cache_unittest.cc b/base/containers/mru_cache_unittest.cc
index 07b0144..d4ee482 100644
--- a/base/containers/mru_cache_unittest.cc
+++ b/base/containers/mru_cache_unittest.cc
@@ -7,7 +7,6 @@
 #include <cstddef>
 #include <memory>
 
-#include "base/memory/ptr_util.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -200,8 +199,8 @@
 
   // First insert and item and then overwrite it.
   static const int kItem1Key = 1;
-  cache.Put(kItem1Key, WrapUnique(new CachedItem(20)));
-  cache.Put(kItem1Key, WrapUnique(new CachedItem(22)));
+  cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
+  cache.Put(kItem1Key, std::make_unique<CachedItem>(22));
 
   // There should still be one item, and one extra live item.
   auto iter = cache.Get(kItem1Key);
@@ -217,8 +216,8 @@
   // go away.
   {
     Cache cache2(Cache::NO_AUTO_EVICT);
-    cache2.Put(1, WrapUnique(new CachedItem(20)));
-    cache2.Put(2, WrapUnique(new CachedItem(20)));
+    cache2.Put(1, std::make_unique<CachedItem>(20));
+    cache2.Put(2, std::make_unique<CachedItem>(20));
   }
 
   // There should be no objects leaked.
@@ -227,8 +226,8 @@
   // Check that Clear() also frees things correctly.
   {
     Cache cache2(Cache::NO_AUTO_EVICT);
-    cache2.Put(1, WrapUnique(new CachedItem(20)));
-    cache2.Put(2, WrapUnique(new CachedItem(20)));
+    cache2.Put(1, std::make_unique<CachedItem>(20));
+    cache2.Put(2, std::make_unique<CachedItem>(20));
     EXPECT_EQ(initial_count + 2, cached_item_live_count);
     cache2.Clear();
     EXPECT_EQ(initial_count, cached_item_live_count);
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc
index e82ef3d..31bb8fc 100644
--- a/base/debug/activity_analyzer.cc
+++ b/base/debug/activity_analyzer.cc
@@ -12,7 +12,6 @@
 #include "base/files/memory_mapped_file.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -108,7 +107,7 @@
     return nullptr;
   }
 
-  return WrapUnique(new GlobalActivityAnalyzer(std::move(allocator)));
+  return std::make_unique<GlobalActivityAnalyzer>(std::move(allocator));
 }
 
 #if !defined(OS_NACL)
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index 164997a..3a870ac 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/format_macros.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/persistent_memory_allocator.h"
 #include "base/strings/string_piece.h"
@@ -45,7 +44,7 @@
 class FeatureListTest : public testing::Test {
  public:
   FeatureListTest() : feature_list_(nullptr) {
-    RegisterFeatureListInstance(WrapUnique(new FeatureList));
+    RegisterFeatureListInstance(std::make_unique<FeatureList>());
   }
   ~FeatureListTest() override { ClearFeatureListInstance(); }
 
diff --git a/base/metrics/persistent_sample_map.cc b/base/metrics/persistent_sample_map.cc
index f38b9d1..e07b7167 100644
--- a/base/metrics/persistent_sample_map.cc
+++ b/base/metrics/persistent_sample_map.cc
@@ -5,7 +5,6 @@
 #include "base/metrics/persistent_sample_map.h"
 
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/persistent_histogram_allocator.h"
 #include "base/numerics/safe_conversions.h"
@@ -154,7 +153,7 @@
   // Have to override "const" in order to make sure all samples have been
   // loaded before trying to iterate over the map.
   const_cast<PersistentSampleMap*>(this)->ImportSamples(-1, true);
-  return WrapUnique(new PersistentSampleMapIterator(sample_counts_));
+  return std::make_unique<PersistentSampleMapIterator>(sample_counts_);
 }
 
 // static
diff --git a/base/metrics/sample_map.cc b/base/metrics/sample_map.cc
index b441afa..f9252386 100644
--- a/base/metrics/sample_map.cc
+++ b/base/metrics/sample_map.cc
@@ -5,7 +5,6 @@
 #include "base/metrics/sample_map.h"
 
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
 
@@ -106,7 +105,7 @@
 }
 
 std::unique_ptr<SampleCountIterator> SampleMap::Iterator() const {
-  return WrapUnique(new SampleMapIterator(sample_counts_));
+  return std::make_unique<SampleMapIterator>(sample_counts_);
 }
 
 bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) {
diff --git a/base/metrics/single_sample_metrics_unittest.cc b/base/metrics/single_sample_metrics_unittest.cc
index d4d5913..974ba7e 100644
--- a/base/metrics/single_sample_metrics_unittest.cc
+++ b/base/metrics/single_sample_metrics_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "base/metrics/single_sample_metrics.h"
 
-#include "base/memory/ptr_util.h"
 #include "base/metrics/dummy_histogram.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -42,14 +41,14 @@
   EXPECT_EQ(factory, SingleSampleMetricsFactory::Get());
 
   // Setting a factory after the default has been instantiated should fail.
-  EXPECT_DCHECK_DEATH(SingleSampleMetricsFactory::SetFactory(
-      WrapUnique<SingleSampleMetricsFactory>(nullptr)));
+  EXPECT_DCHECK_DEATH(SingleSampleMetricsFactory::SetFactory(nullptr));
 }
 
 TEST_F(SingleSampleMetricsTest, CustomFactoryGetSet) {
-  SingleSampleMetricsFactory* factory = new DefaultSingleSampleMetricsFactory();
-  SingleSampleMetricsFactory::SetFactory(WrapUnique(factory));
-  EXPECT_EQ(factory, SingleSampleMetricsFactory::Get());
+  auto factory = std::make_unique<DefaultSingleSampleMetricsFactory>();
+  SingleSampleMetricsFactory* factory_raw = factory.get();
+  SingleSampleMetricsFactory::SetFactory(std::move(factory));
+  EXPECT_EQ(factory_raw, SingleSampleMetricsFactory::Get());
 }
 
 TEST_F(SingleSampleMetricsTest, DefaultSingleSampleMetricNoValue) {
diff --git a/base/profiler/win32_stack_frame_unwinder.cc b/base/profiler/win32_stack_frame_unwinder.cc
index a337c16..b5db93b4 100644
--- a/base/profiler/win32_stack_frame_unwinder.cc
+++ b/base/profiler/win32_stack_frame_unwinder.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 
 namespace base {
 
@@ -112,7 +111,7 @@
 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
 
 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
-    : Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {}
+    : Win32StackFrameUnwinder(std::make_unique<Win32UnwindFunctions>()) {}
 
 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
 
diff --git a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
index a49dc895..40c70f2 100644
--- a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -5,7 +5,6 @@
 #include "base/task/task_scheduler/scheduler_worker_stack.h"
 
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/task_scheduler/scheduler_worker.h"
 #include "base/task/task_scheduler/sequence.h"
@@ -45,15 +44,15 @@
  protected:
   void SetUp() override {
     worker_a_ = MakeRefCounted<SchedulerWorker>(
-        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        ThreadPriority::NORMAL, std::make_unique<MockSchedulerWorkerDelegate>(),
         task_tracker_.GetTrackedRef());
     ASSERT_TRUE(worker_a_);
     worker_b_ = MakeRefCounted<SchedulerWorker>(
-        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        ThreadPriority::NORMAL, std::make_unique<MockSchedulerWorkerDelegate>(),
         task_tracker_.GetTrackedRef());
     ASSERT_TRUE(worker_b_);
     worker_c_ = MakeRefCounted<SchedulerWorker>(
-        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        ThreadPriority::NORMAL, std::make_unique<MockSchedulerWorkerDelegate>(),
         task_tracker_.GetTrackedRef());
     ASSERT_TRUE(worker_c_);
   }
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
index 093f5ed..0b53e80 100644
--- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/cfi_buildflags.h"
 #include "base/debug/stack_trace.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/synchronization/waitable_event.h"
@@ -451,9 +450,9 @@
   StartTaskScheduler();
   std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
   for (const auto& traits_execution_mode_pair : GetTraitsExecutionModePairs()) {
-    threads_posting_tasks.push_back(WrapUnique(
-        new ThreadPostingTasks(&scheduler_, traits_execution_mode_pair.traits,
-                               traits_execution_mode_pair.execution_mode)));
+    threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>(
+        &scheduler_, traits_execution_mode_pair.traits,
+        traits_execution_mode_pair.execution_mode));
     threads_posting_tasks.back()->Start();
   }
 
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
index 6461b0fe..a23260d 100644
--- a/base/test/trace_event_analyzer_unittest.cc
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include "base/bind.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
@@ -103,7 +102,7 @@
   event.arg_numbers["int"] = static_cast<double>(int_num);
   event.arg_numbers["double"] = double_num;
   event.arg_strings["string"] = str;
-  event.arg_values["dict"] = WrapUnique(new base::DictionaryValue());
+  event.arg_values["dict"] = std::make_unique<base::DictionaryValue>();
 
   ASSERT_TRUE(event.HasNumberArg("false"));
   ASSERT_TRUE(event.HasNumberArg("true"));
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index f30a0e8..abfb5166 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/command_line.h"
 #include "base/debug/thread_heap_usage_tracker.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
@@ -425,11 +424,11 @@
   // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be
   // invoked a number of times equal to its index.
   for (uint32_t i = kNumInitialThreads; i > 0; --i) {
-    threads.push_back(WrapUnique(new Thread("test thread")));
+    threads.push_back(std::make_unique<Thread>("test thread"));
     auto* thread = threads.back().get();
     thread->Start();
     scoped_refptr<SingleThreadTaskRunner> task_runner = thread->task_runner();
-    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
     auto* mdp = mdps.back().get();
     RegisterDumpProvider(mdp, task_runner, kDefaultOptions);
     EXPECT_CALL(*mdp, OnMemoryDump(_, _))
@@ -602,9 +601,8 @@
   std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
 
   for (int i = 0; i < 2; i++) {
-    threads.push_back(
-        WrapUnique(new TestIOThread(TestIOThread::kAutoStart)));
-    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    threads.push_back(std::make_unique<TestIOThread>(TestIOThread::kAutoStart));
+    mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
     RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
                          kDefaultOptions);
   }
@@ -651,9 +649,8 @@
   std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
 
   for (int i = 0; i < 2; i++) {
-    threads.push_back(
-        WrapUnique(new TestIOThread(TestIOThread::kAutoStart)));
-    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    threads.push_back(std::make_unique<TestIOThread>(TestIOThread::kAutoStart));
+    mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
     RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
                          kDefaultOptions);
   }
diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/trace_event_argument_unittest.cc
index 448b2d56..69d203a 100644
--- a/base/trace_event/trace_event_argument_unittest.cc
+++ b/base/trace_event/trace_event_argument_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -101,14 +100,14 @@
   Value bool_value(true);
   Value double_value(42.0f);
 
-  auto dict_value = WrapUnique(new DictionaryValue);
+  auto dict_value = std::make_unique<DictionaryValue>();
   dict_value->SetBoolean("bool", true);
   dict_value->SetInteger("int", 42);
   dict_value->SetDouble("double", 42.0f);
   dict_value->SetString("string", std::string("a") + "b");
   dict_value->SetString("string", std::string("a") + "b");
 
-  auto list_value = WrapUnique(new ListValue);
+  auto list_value = std::make_unique<ListValue>();
   list_value->AppendBoolean(false);
   list_value->AppendInteger(1);
   list_value->AppendString("in_list");
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index c9185fd..b9239744 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "base/containers/adapters.h"
-#include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
@@ -1208,7 +1207,7 @@
 
   std::unique_ptr<ListValue> list(new ListValue);
   list->Append(std::make_unique<Value>());
-  list->Append(WrapUnique(new DictionaryValue));
+  list->Append(std::make_unique<DictionaryValue>());
   auto list_copy = std::make_unique<Value>(list->Clone());
 
   ListValue* list_weak = dv.SetList("f", std::move(list));
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 9462bfb..c90e5639 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -6,6 +6,7 @@
 
 import optparse
 import os
+import shutil
 import sys
 import tempfile
 
@@ -56,15 +57,23 @@
                          'included by --proguard-configs, but that should '
                          'not actually be included.')
   parser.add_option('--mapping', help='Path to proguard mapping to apply.')
+  parser.add_option('--mapping-output',
+                    help='Path for proguard to output mapping file to.')
   parser.add_option('--classpath', action='append',
                     help='Classpath for proguard.')
   parser.add_option('--enable-dangerous-optimizations', action='store_true',
                     help='Enable optimizations which are known to have issues.')
+  parser.add_option('--main-dex-rules-path', action='append',
+                    help='Paths to main dex rules for multidex'
+                         '- only works with R8.')
   parser.add_option('--verbose', '-v', action='store_true',
                     help='Print all proguard output')
 
   options, _ = parser.parse_args(args)
 
+  assert not options.main_dex_rules_path or options.r8_path, \
+      "R8 must be enabled to pass main dex rules."
+
   classpath = []
   for arg in options.classpath:
     classpath += build_utils.ParseGnList(arg)
@@ -79,16 +88,38 @@
 
   options.input_paths = build_utils.ParseGnList(options.input_paths)
 
+  if not options.mapping_output:
+    options.mapping_output = options.output_path + ".mapping"
+
   return options
 
 
-def _CreateR8Command(options, map_output_path):
+def _MoveTempDexFile(tmp_dex_dir, dex_path):
+  """Move the temp dex file out of |tmp_dex_dir|.
+
+  Args:
+    tmp_dex_dir: Path to temporary directory created with tempfile.mkdtemp().
+      The directory should have just a single file.
+    dex_path: Target path to move dex file to.
+
+  Raises:
+    Exception if there are multiple files in |tmp_dex_dir|.
+  """
+  tempfiles = os.listdir(tmp_dex_dir)
+  if len(tempfiles) > 1:
+    raise Exception('%d files created, expected 1' % len(tempfiles))
+
+  tmp_dex_path = os.path.join(tmp_dex_dir, tempfiles[0])
+  shutil.move(tmp_dex_path, dex_path)
+
+
+def _CreateR8Command(options, map_output_path, output_dir):
   # TODO: R8 needs -applymapping equivalent.
   cmd = [
     'java', '-jar', options.r8_path,
     '--no-desugaring',
-    '--classfile',
-    '--output', options.output_path,
+    '--no-data-resources',
+    '--output', output_dir,
     '--pg-map-output', map_output_path,
   ]
 
@@ -101,6 +132,10 @@
   for config_file in options.proguard_configs:
     cmd += ['--pg-conf', config_file]
 
+  if options.main_dex_rules_path:
+    for main_dex_rule in options.main_dex_rules_path:
+      cmd += ['--main-dex-rules', main_dex_rule]
+
   cmd += options.input_paths
   return cmd
 
@@ -114,6 +149,7 @@
   proguard.configs(options.proguard_configs)
   proguard.config_exclusions(options.proguard_config_exclusions)
   proguard.outjar(options.output_path)
+  proguard.mapping_output(options.mapping_output)
 
   # If a jar is part of input no need to include it as library jar.
   classpath = [
@@ -127,12 +163,17 @@
   # TODO(agrieve): Remove proguard usages.
   if options.r8_path:
     with tempfile.NamedTemporaryFile() as mapping_temp:
-      # R8 will output mapping file to this temp file
-      cmd = _CreateR8Command(options, mapping_temp.name)
-      build_utils.CheckOutput(cmd)
+      if options.output_path.endswith('.dex'):
+        with build_utils.TempDir() as tmp_dex_dir:
+          cmd = _CreateR8Command(options, mapping_temp.name, tmp_dex_dir)
+          build_utils.CheckOutput(cmd)
+          _MoveTempDexFile(tmp_dex_dir, options.output_path)
+      else:
+        cmd = _CreateR8Command(options, mapping_temp.name, options.output_path)
+        build_utils.CheckOutput(cmd)
 
       # Copy the mapping file back to where it should be.
-      map_path = options.output_path + ".mapping"
+      map_path = options.mapping_output
       with build_utils.AtomicOutput(map_path) as mapping:
         # Mapping files generated by R8 include comments that may break
         # some of our tooling so remove those.
diff --git a/build/android/gyp/util/proguard_util.py b/build/android/gyp/util/proguard_util.py
index 8a6e785..bce124e 100644
--- a/build/android/gyp/util/proguard_util.py
+++ b/build/android/gyp/util/proguard_util.py
@@ -51,6 +51,7 @@
     self._configs = None
     self._config_exclusions = None
     self._outjar = None
+    self._mapping_output = None
     self._verbose = False
     self._disabled_optimizations = []
 
@@ -58,6 +59,10 @@
     assert self._outjar is None
     self._outjar = path
 
+  def mapping_output(self, path):
+    assert self._mapping_output is None
+    self._mapping_output = path
+
   def mapping(self, path):
     assert self._mapping is None
     assert os.path.exists(path), path
@@ -124,7 +129,7 @@
       '-outjars', self._outjar,
       '-printseeds', self._outjar + '.seeds',
       '-printusage', self._outjar + '.usage',
-      '-printmapping', self._outjar + '.mapping',
+      '-printmapping', self._mapping_output,
     ]
 
     if self._verbose:
@@ -156,7 +161,7 @@
     return [
         self._outjar,
         self._outjar + '.flags',
-        self._outjar + '.mapping',
+        self._mapping_output,
         self._outjar + '.seeds',
         self._outjar + '.usage',
     ]
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index a2387da7..f4d25bf 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -860,7 +860,7 @@
       help='Whether proguard is enabled for this apk or bundle module.')
   parser.add_option('--proguard-configs',
       help='GN-list of proguard flag files to use in final apk.')
-  parser.add_option('--proguard-output-jar-path',
+  parser.add_option('--proguard-mapping-path',
       help='Path to jar created by ProGuard step')
   parser.add_option('--fail',
       help='GN-list of error message lines to fail with.')
@@ -1257,8 +1257,8 @@
                                                deps_proguard_disabled))
     else:
       deps_info['proguard_enabled'] = bool(options.proguard_enabled)
-      if options.proguard_output_jar_path:
-        deps_info['proguard_output_jar_path'] = options.proguard_output_jar_path
+      if options.proguard_mapping_path:
+        deps_info['proguard_mapping_path'] = options.proguard_mapping_path
 
   # The java code for an instrumentation test apk is assembled differently for
   # ProGuard vs. non-ProGuard.
@@ -1287,7 +1287,7 @@
                         if p not in extra_jars)
       tested_apk_config = GetDepConfig(options.tested_apk_config)
       deps_info['proguard_under_test_mapping'] = (
-          tested_apk_config['proguard_output_jar_path'] + '.mapping')
+          tested_apk_config['proguard_mapping_path'])
     elif options.proguard_enabled:
       # Not sure why you'd want to proguard the test apk when the under-test apk
       # is not proguarded, but it's easy enough to support.
diff --git a/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java b/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java
index e54054cb..134a152 100644
--- a/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java
+++ b/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java
@@ -34,13 +34,17 @@
             // Eagerly match logcat prefix to avoid conflicting with the patterns below.
             LOGCAT_PREFIX
             + "(?:"
-            // Based on default ReTrace regex, but with "at" changed to to allow :
-            // E.g.: 06-22 13:58:02.895  4674  4674 E THREAD_STATE:     bLA.a(PG:173)
+            // Based on default ReTrace regex, but with whitespaces allowed in file:line parentheses
+            // and "at" changed to to allow :
+            // E.g.: 06-22 13:58:02.895  4674  4674 E THREAD_STATE:     bLA.a( PG : 173 )
             // Normal stack trace lines look like:
             // \tat org.chromium.chrome.browser.tab.Tab.handleJavaCrash(Tab.java:682)
-            + "(?:.*?(?::|\\bat)\\s+%c\\.%m\\s*\\(%s(?::%l)?\\))|"
+            + "(?:.*?(?::|\\bat)\\s+%c\\.%m\\s*\\(\\s*%s(?:\\s*:\\s*%l\\s*)?\\))|"
             // E.g.: VFY: unable to resolve new-instance 3810 (LSome/Framework/Class;) in Lfoo/Bar;
             + "(?:.*L%C;.*)|"
+            // E.g.: Caused by: java.lang.NullPointerException: Attempt to read from field 'int bLA'
+            // on a null object reference
+            + "(?:.*NullPointerException.*[\"']%t\\s*%c\\.(?:%f|%m\\(%a\\))[\"'].*)|"
             // E.g.: END SomeTestClass#someMethod
             + "(?:.*?%c#%m.*?)|"
             // E.g.: The member "Foo.bar"
diff --git a/build/android/stacktrace/java_deobfuscate_test.py b/build/android/stacktrace/java_deobfuscate_test.py
index 6ee40ce..fc11d97 100755
--- a/build/android/stacktrace/java_deobfuscate_test.py
+++ b/build/android/stacktrace/java_deobfuscate_test.py
@@ -27,40 +27,56 @@
 TEST_MAP = """\
 this.was.Deobfuscated -> FOO:
     int[] FontFamily -> a
-    1:3:void someMethod(int,android.os.Bundle):65:65 -> bar
+    1:3:void someMethod(int,android.os.Bundle):65:67 -> bar
 """
 
-TEST_DATA = """\
-Here is a FOO
-Here is a FOO baz
-Here is a "FOO" baz
-Here is a "FOO.bar" baz
-Here it is: FOO
-Here it is: FOO.bar
-SomeError: SomeFrameworkClass in isTestClass for FOO
-Here is a FOO.bar
-Here is a FOO.bar baz
-END FOO#bar
-new-instance 3810 (LSome/Framework/Class;) in LFOO;
-FOO: Error message
-\tat FOO.bar(PG:1)
-""".splitlines(True)
+TEST_DATA = [
+    "Here is a FOO",
+    "Here is a FOO baz",
+    "Here is a \"FOO\" baz",
+    "Here is a \"FOO.bar\" baz",
+    "Here it is: FOO",
+    "Here it is: FOO.bar",
+    "SomeError: SomeFrameworkClass in isTestClass for FOO",
+    "Here is a FOO.bar",
+    "Here is a FOO.bar baz",
+    "END FOO#bar",
+    "new-instance 3810 (LSome/Framework/Class;) in LFOO;",
+    "FOO: Error message",
+    "\tat FOO.bar(PG:1)",
+    "\t at\t FOO.bar\t (\t PG:\t 1\t )",
+    ("Unable to start activity ComponentInfo{garbage.in/here.test}:"
+     " java.lang.NullPointerException: Attempt to invoke interface method 'void"
+     " FOO.bar(int,android.os.Bundle)' on a null object reference"),
+    ("Caused by: java.lang.NullPointerException: Attempt to read from field"
+     " 'int[] FOO.a' on a null object reference"),
+]
 
-EXPECTED_OUTPUT = """\
-Here is a this.was.Deobfuscated
-Here is a FOO baz
-Here is a "this.was.Deobfuscated" baz
-Here is a "this.was.Deobfuscated.someMethod" baz
-Here it is: this.was.Deobfuscated
-Here it is: this.was.Deobfuscated.someMethod
-SomeError: SomeFrameworkClass in isTestClass for this.was.Deobfuscated
-Here is a this.was.Deobfuscated.someMethod
-Here is a FOO.bar baz
-END this.was.Deobfuscated#someMethod
-new-instance 3810 (LSome/Framework/Class;) in Lthis/was/Deobfuscated;
-this.was.Deobfuscated: Error message
-\tat this.was.Deobfuscated.someMethod(Deobfuscated.java:65)
-""".splitlines(True)
+EXPECTED_OUTPUT = [
+    "Here is a this.was.Deobfuscated",
+    "Here is a FOO baz",
+    "Here is a \"this.was.Deobfuscated\" baz",
+    "Here is a \"this.was.Deobfuscated.someMethod\" baz",
+    "Here it is: this.was.Deobfuscated",
+    "Here it is: this.was.Deobfuscated.someMethod",
+    "SomeError: SomeFrameworkClass in isTestClass for this.was.Deobfuscated",
+    "Here is a this.was.Deobfuscated.someMethod",
+    "Here is a FOO.bar baz",
+    "END this.was.Deobfuscated#someMethod",
+    "new-instance 3810 (LSome/Framework/Class;) in Lthis/was/Deobfuscated;",
+    "this.was.Deobfuscated: Error message",
+    "\tat this.was.Deobfuscated.someMethod(Deobfuscated.java:65)",
+    ("\t at\t this.was.Deobfuscated.someMethod\t "
+     "(\t Deobfuscated.java:\t 65\t )"),
+    ("Unable to start activity ComponentInfo{garbage.in/here.test}:"
+     " java.lang.NullPointerException: Attempt to invoke interface method"
+     " 'void this.was.Deobfuscated.someMethod(int,android.os.Bundle)' on a null"
+     " object reference"),
+    ("Caused by: java.lang.NullPointerException: Attempt to read from field"
+     " 'int[] this.was.Deobfuscated.FontFamily' on a null object reference"),
+]
+TEST_DATA = [s + "\n" for s in TEST_DATA]
+EXPECTED_OUTPUT = [s + "\n" for s in EXPECTED_OUTPUT]
 
 
 class JavaDeobfuscateTest(unittest.TestCase):
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index d393bc6..6995161 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -419,11 +419,10 @@
     if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) {
       args += [ "--proguard-enabled" ]
     }
-    if (defined(invoker.proguard_output_jar_path)) {
-      _rebased_proguard_output_jar_path =
-          rebase_path(invoker.proguard_output_jar_path, root_build_dir)
-      args +=
-          [ "--proguard-output-jar-path=$_rebased_proguard_output_jar_path" ]
+    if (defined(invoker.proguard_mapping_path)) {
+      _rebased_proguard_mapping_path =
+          rebase_path(invoker.proguard_mapping_path, root_build_dir)
+      args += [ "--proguard-mapping-path=$_rebased_proguard_mapping_path" ]
     }
     if (defined(invoker.proguard_configs)) {
       _rebased_proguard_configs =
@@ -944,7 +943,7 @@
       # http://crbug.com/725224. Fix for bots running out of memory.
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
-      _output_jar_path = invoker.output_jar_path
+      _output_path = invoker.output_path
       _proguard_jar_path = _default_proguard_jar_path
       if (defined(invoker.proguard_jar_path)) {
         _proguard_jar_path = invoker.proguard_jar_path
@@ -957,10 +956,14 @@
       if (defined(invoker.inputs)) {
         inputs += invoker.inputs
       }
+      _mapping_path = "$_output_path.mapping"
+      if (defined(invoker.proguard_mapping_path)) {
+        _mapping_path = invoker.proguard_mapping_path
+      }
       depfile = "${target_gen_dir}/${target_name}.d"
       outputs = [
-        _output_jar_path,
-        "$_output_jar_path.mapping",
+        _output_path,
+        _mapping_path,
       ]
       _rebased_build_config = rebase_path(invoker.build_config, root_build_dir)
       args = [
@@ -969,7 +972,9 @@
         "--proguard-path",
         rebase_path(_proguard_jar_path, root_build_dir),
         "--output-path",
-        rebase_path(_output_jar_path, root_build_dir),
+        rebase_path(_output_path, root_build_dir),
+        "--mapping-output",
+        rebase_path(_mapping_path, root_build_dir),
         "--classpath",
         "@FileArg($_rebased_build_config:deps_info:proguard_classpath_jars)",
         "--classpath",
@@ -1058,72 +1063,198 @@
   }
 
   template("dex") {
-    _enable_multidex =
-        defined(invoker.enable_multidex) && invoker.enable_multidex
+    assert(defined(invoker.output))
 
-    if (_enable_multidex) {
-      _main_dex_list_path = invoker.output + ".main_dex_list"
-      _main_dex_list_target_name = "${target_name}__main_dex_list"
-      action_with_pydeps(_main_dex_list_target_name) {
+    _proguard_enabled =
+        defined(invoker.proguard_enabled) && invoker.proguard_enabled
+    _proguarding_with_r8 = _proguard_enabled && experimental_r8_path != ""
+
+    assert(!(defined(invoker.input_jars) && _proguard_enabled),
+           "input_jars can't be specified when proguarding a dex.")
+
+    if (!_proguarding_with_r8) {
+      _dexing_jars = []
+      if (defined(invoker.input_jars)) {
+        _dexing_jars += invoker.input_jars
+      }
+    }
+
+    if (_proguard_enabled) {
+      if (defined(invoker.enable_multidex)) {
+        assert(invoker.enable_multidex || !invoker.enable_multidex)
+        if (defined(invoker.extra_main_dex_proguard_config)) {
+          assert(invoker.extra_main_dex_proguard_config != [])
+        }
+        if (defined(invoker.negative_main_dex_globs)) {
+          assert(invoker.negative_main_dex_globs != [])
+        }
+      }
+      if (_proguarding_with_r8) {
+        _proguard_output_path = invoker.output
+        _proguard_target_name = target_name
+      } else {
+        _proguard_output_path = invoker.output + ".proguard.jar"
+        _proguard_target_name = "${target_name}__proguard"
+        _dexing_jars += [ _proguard_output_path ]
+      }
+
+      proguard(_proguard_target_name) {
+        forward_variables_from(invoker,
+                               [
+                                 "proguard_jar_path",
+                                 "deps",
+                                 "build_config",
+                                 "proguard_mapping_path",
+                                 "testonly",
+                               ])
+        inputs = []
+        if (defined(invoker.inputs)) {
+          inputs += invoker.inputs
+        }
+        if (defined(invoker.proguard_configs)) {
+          inputs += invoker.proguard_configs
+        }
+
+        _rebased_build_config = rebase_path(build_config, root_build_dir)
+        args = [
+          "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
+          "--input-paths=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)",
+        ]
+        if (defined(invoker.proguard_config_exclusions)) {
+          _rebased_proguard_config_exclusions =
+              rebase_path(invoker.proguard_config_exclusions, root_build_dir)
+          args += [
+            "--proguard-config-exclusions=$_rebased_proguard_config_exclusions",
+          ]
+        }
+        if (defined(invoker.proguard_args)) {
+          args += invoker.proguard_args
+        }
+
+        output_path = _proguard_output_path
+      }
+    }
+
+    if (!_proguarding_with_r8) {
+      _enable_multidex =
+          defined(invoker.enable_multidex) && invoker.enable_multidex
+      if (_enable_multidex) {
+        _main_dex_list_path = invoker.output + ".main_dex_list"
+        _main_dex_list_target_name = "${target_name}__main_dex_list"
+        action_with_pydeps(_main_dex_list_target_name) {
+          forward_variables_from(invoker,
+                                 [
+                                   "deps",
+                                   "testonly",
+                                 ])
+
+          script = "//build/android/gyp/main_dex_list.py"
+          depfile = "$target_gen_dir/$target_name.d"
+
+          # http://crbug.com/725224. Fix for bots running out of memory.
+          pool = "//build/toolchain:link_pool($default_toolchain)"
+
+          main_dex_rules = "//build/android/main_dex_classes.flags"
+
+          if (defined(invoker.proguard_jar_path)) {
+            _proguard_jar_path = invoker.proguard_jar_path
+          } else {
+            _proguard_jar_path = _default_proguard_jar_path
+          }
+
+          _shrinked_android = "$android_sdk_build_tools/lib/shrinkedAndroid.jar"
+          _dx = "$android_sdk_build_tools/lib/dx.jar"
+          inputs = [
+            main_dex_rules,
+            _dx,
+            _proguard_jar_path,
+            _shrinked_android,
+          ]
+
+          outputs = [
+            _main_dex_list_path,
+          ]
+
+          args = [
+            "--depfile",
+            rebase_path(depfile, root_build_dir),
+            "--dx-path",
+            rebase_path(_dx, root_build_dir),
+            "--shrinked-android-path",
+            rebase_path(_shrinked_android, root_build_dir),
+            "--main-dex-list-path",
+            rebase_path(_main_dex_list_path, root_build_dir),
+            "--main-dex-rules-path",
+            rebase_path(main_dex_rules, root_build_dir),
+            "--proguard-path",
+            rebase_path(_proguard_jar_path, root_build_dir),
+          ]
+
+          if (defined(invoker.extra_main_dex_proguard_config)) {
+            inputs += [ invoker.extra_main_dex_proguard_config ]
+            args += [
+              "--main-dex-rules-path",
+              rebase_path(invoker.extra_main_dex_proguard_config,
+                          root_build_dir),
+            ]
+          }
+
+          if (_proguard_enabled) {
+            deps += [ ":${_proguard_target_name}" ]
+          }
+
+          if (defined(invoker.negative_main_dex_globs)) {
+            args += [
+              "--negative-main-dex-globs=${invoker.negative_main_dex_globs}",
+            ]
+          }
+
+          if (defined(invoker.input_jars_file_arg)) {
+            inputs += [ invoker.build_config ]
+            args += [ "--inputs=${invoker.input_jars_file_arg}" ]
+          }
+
+          inputs += _dexing_jars
+          if (_dexing_jars != []) {
+            args += rebase_path(_dexing_jars, root_build_dir)
+          }
+        }
+      }
+
+      action_with_pydeps(target_name) {
         forward_variables_from(invoker,
                                [
                                  "deps",
                                  "testonly",
                                ])
-
-        script = "//build/android/gyp/main_dex_list.py"
+        script = "//build/android/gyp/dex.py"
         depfile = "$target_gen_dir/$target_name.d"
-
-        # http://crbug.com/725224. Fix for bots running out of memory.
-        pool = "//build/toolchain:link_pool($default_toolchain)"
-
-        main_dex_rules = "//build/android/main_dex_classes.flags"
-
-        if (defined(invoker.proguard_jar_path)) {
-          _proguard_jar_path = invoker.proguard_jar_path
-        } else {
-          _proguard_jar_path = _default_proguard_jar_path
-        }
-
-        _shrinked_android = "$android_sdk_build_tools/lib/shrinkedAndroid.jar"
-        _dx = "$android_sdk_build_tools/lib/dx.jar"
-        inputs = [
-          main_dex_rules,
-          _dx,
-          _proguard_jar_path,
-          _shrinked_android,
-        ]
-
+        inputs = []
         outputs = [
-          _main_dex_list_path,
+          invoker.output,
         ]
 
+        _rebased_output = rebase_path(invoker.output, root_build_dir)
+
         args = [
           "--depfile",
           rebase_path(depfile, root_build_dir),
-          "--dx-path",
-          rebase_path(_dx, root_build_dir),
-          "--shrinked-android-path",
-          rebase_path(_shrinked_android, root_build_dir),
-          "--main-dex-list-path",
-          rebase_path(_main_dex_list_path, root_build_dir),
-          "--main-dex-rules-path",
-          rebase_path(main_dex_rules, root_build_dir),
-          "--proguard-path",
-          rebase_path(_proguard_jar_path, root_build_dir),
+          "--dex-path",
+          _rebased_output,
         ]
 
-        if (defined(invoker.extra_main_dex_proguard_config)) {
-          inputs += [ invoker.extra_main_dex_proguard_config ]
-          args += [
-            "--main-dex-rules-path",
-            rebase_path(invoker.extra_main_dex_proguard_config, root_build_dir),
-          ]
+        if (_proguard_enabled) {
+          deps += [ ":${_proguard_target_name}" ]
         }
 
-        if (defined(invoker.negative_main_dex_globs)) {
-          args +=
-              [ "--negative-main-dex-globs=${invoker.negative_main_dex_globs}" ]
+        if (_enable_multidex) {
+          args += [
+            "--multi-dex",
+            "--main-dex-list-path",
+            rebase_path(_main_dex_list_path, root_build_dir),
+          ]
+          deps += [ ":${_main_dex_list_target_name}" ]
+          inputs += [ _main_dex_list_path ]
         }
 
         if (defined(invoker.input_jars_file_arg)) {
@@ -1131,94 +1262,46 @@
           args += [ "--inputs=${invoker.input_jars_file_arg}" ]
         }
 
-        if (defined(invoker.input_jars)) {
-          inputs += invoker.input_jars
-          args += rebase_path(invoker.input_jars, root_build_dir)
+        inputs += _dexing_jars
+        if (_dexing_jars != []) {
+          args += rebase_path(_dexing_jars, root_build_dir)
         }
-      }
-    }
 
-    assert(defined(invoker.output))
-    action_with_pydeps(target_name) {
-      forward_variables_from(invoker,
-                             [
-                               "deps",
-                               "testonly",
-                             ])
-      script = "//build/android/gyp/dex.py"
-      depfile = "$target_gen_dir/$target_name.d"
-      inputs = []
-      outputs = [
-        invoker.output,
-      ]
+        if (defined(invoker.dexlayout_profile)) {
+          args += [
+            "--dexlayout-profile",
+            rebase_path(invoker.dexlayout_profile, root_build_dir),
+            "--dexlayout-path",
+            rebase_path(_dexlayout_path, root_build_dir),
+            "--profman-path",
+            rebase_path(_profman_path, root_build_dir),
+          ]
+          inputs += [
+            _dexlayout_path,
+            _profman_path,
+            invoker.dexlayout_profile,
+          ]
+          inputs += _default_art_libs
+        }
 
-      if (defined(invoker.use_pool) && invoker.use_pool) {
-        pool = "//build/toolchain:link_pool($default_toolchain)"
-      }
+        if (!is_java_debug) {
+          args += [ "--release" ]
+        }
 
-      _rebased_output = rebase_path(invoker.output, root_build_dir)
+        if (defined(invoker.min_sdk_version)) {
+          args += [
+            "--min-api",
+            "${invoker.min_sdk_version}",
+          ]
+        }
 
-      args = [
-        "--depfile",
-        rebase_path(depfile, root_build_dir),
-        "--dex-path",
-        _rebased_output,
-      ]
-
-      if (defined(invoker.dexlayout_profile)) {
+        _d8_path = "//third_party/r8/lib/d8.jar"
+        inputs += [ _d8_path ]
         args += [
-          "--dexlayout-profile",
-          rebase_path(invoker.dexlayout_profile, root_build_dir),
-          "--dexlayout-path",
-          rebase_path(_dexlayout_path, root_build_dir),
-          "--profman-path",
-          rebase_path(_profman_path, root_build_dir),
-        ]
-        inputs += [
-          _dexlayout_path,
-          _profman_path,
-          invoker.dexlayout_profile,
-        ]
-        inputs += _default_art_libs
-      }
-
-      if (_enable_multidex) {
-        args += [
-          "--multi-dex",
-          "--main-dex-list-path",
-          rebase_path(_main_dex_list_path, root_build_dir),
-        ]
-        deps += [ ":${_main_dex_list_target_name}" ]
-        inputs += [ _main_dex_list_path ]
-      }
-
-      if (defined(invoker.input_jars_file_arg)) {
-        inputs += [ invoker.build_config ]
-        args += [ "--inputs=${invoker.input_jars_file_arg}" ]
-      }
-
-      if (defined(invoker.input_jars)) {
-        inputs += invoker.input_jars
-        args += rebase_path(invoker.input_jars, root_build_dir)
-      }
-
-      if (!is_java_debug) {
-        args += [ "--release" ]
-      }
-
-      if (defined(invoker.min_sdk_version)) {
-        args += [
-          "--min-api",
-          "${invoker.min_sdk_version}",
+          "--d8-jar-path",
+          rebase_path(_d8_path, root_build_dir),
         ]
       }
-
-      _d8_path = "//third_party/r8/lib/d8.jar"
-      inputs += [ _d8_path ]
-      args += [
-        "--d8-jar-path",
-        rebase_path(_d8_path, root_build_dir),
-      ]
     }
   }
 
@@ -2786,7 +2869,7 @@
   #    output APK.
   #  incremental_install_json_path: If incremental_allowed, path to the output
   #    incremental install json configuration file.
-  #  proguard_output_jar_path: Path to .jar file produced from ProGuard step.
+  #  proguard_mapping_path: Path to .mapping file produced from ProGuard step.
   #  shared_libraries_runtime_deps_file: Optional. Path to a file listing the
   #    native shared libraries required at runtime by the APK.
   #  secondary_abi_shared_libraries_runtime_deps_file:
@@ -2982,7 +3065,7 @@
                                "main_class",
                                "proguard_configs",
                                "proguard_enabled",
-                               "proguard_output_jar_path",
+                               "proguard_mapping_path",
                                "secondary_abi_loadable_modules",
                                "type",
                              ])
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index c7693e0..d7e92f0a 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1406,18 +1406,12 @@
   #
   # Variables:
   #   output: Path to the output jar.
-  #   dex_path: Path to dex()'ed output (optional).
   #   override_build_config: Use a pre-existing .build_config. Must be of type
   #     "apk".
   #   use_interface_jars: Use all dependent interface .jars rather than
   #     implementation .jars.
   #   use_unprocessed_jars: Use unprocessed / undesugared .jars.
   #   direct_deps_only: Do not recurse on deps.
-  #   proguard_enabled: Whether to run ProGuard on resulting jar.
-  #   proguard_configs: List of proguard configs.
-  #   proguard_jar_path: The path to proguard.jar you wish to use. If undefined,
-  #     the proguard used will be the checked in one in //third_party/proguard.
-  #   alternative_android_sdk_jar: System jar to use when proguard is enabled.
   #
   # Example
   #   dist_jar("lib_fatjar") {
@@ -1435,29 +1429,16 @@
         !defined(invoker.supports_android) || invoker.supports_android
     _requires_android =
         defined(invoker.requires_android) && invoker.requires_android
-    _proguard_enabled =
-        defined(invoker.proguard_enabled) && invoker.proguard_enabled
     _use_interface_jars =
         defined(invoker.use_interface_jars) && invoker.use_interface_jars
     _use_unprocessed_jars =
         defined(invoker.use_unprocessed_jars) && invoker.use_unprocessed_jars
     _direct_deps_only =
         defined(invoker.direct_deps_only) && invoker.direct_deps_only
-    assert(!(_proguard_enabled && _use_interface_jars),
-           "Cannot set both proguard_enabled and use_interface_jars")
-    assert(!(_proguard_enabled && _direct_deps_only),
-           "Cannot set both proguard_enabled and direct_deps_only")
     assert(!(_use_unprocessed_jars && _use_interface_jars),
            "Cannot set both use_interface_jars and use_unprocessed_jars")
 
     _jar_target_name = target_name
-    if (defined(invoker.dex_path)) {
-      if (_proguard_enabled) {
-        _jar_target_name = "${target_name}__proguard"
-      } else {
-        _jar_target_name = "${target_name}__dist_jar"
-      }
-    }
 
     _deps = []
     if (defined(invoker.deps)) {
@@ -1481,11 +1462,6 @@
 
       write_build_config(_build_config_target_name) {
         type = "dist_jar"
-        forward_variables_from(invoker,
-                               [
-                                 "proguard_enabled",
-                                 "proguard_configs",
-                               ])
         supports_android = _supports_android
         requires_android = _requires_android
         possible_config_deps = _deps
@@ -1496,89 +1472,99 @@
     }
 
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
-    if (_proguard_enabled) {
-      proguard(_jar_target_name) {
-        forward_variables_from(invoker,
-                               [
-                                 "data",
-                                 "proguard_jar_path",
-                               ])
-        build_config = _build_config
-        deps = _deps
+    action_with_pydeps(_jar_target_name) {
+      forward_variables_from(invoker, [ "data" ])
+      script = "//build/android/gyp/create_dist_jar.py"
+      depfile = "$target_gen_dir/$target_name.d"
+      deps = _deps
 
-        # Although these will be listed as deps in the depfile, they must also
-        # appear here so that "gn analyze" knows about them.
-        # https://crbug.com/827197
-        inputs = []
-        if (defined(invoker.proguard_configs)) {
-          inputs += invoker.proguard_configs
-        }
+      inputs = [
+        _build_config,
+      ]
 
-        output_jar_path = invoker.output
-        args = [
-          "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
-          "--input-paths=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)",
-        ]
-        if (defined(invoker.proguard_config_exclusions)) {
-          _rebased_proguard_config_exclusions =
-              rebase_path(invoker.proguard_config_exclusions, root_build_dir)
+      outputs = [
+        invoker.output,
+      ]
+
+      args = [
+        "--depfile",
+        rebase_path(depfile, root_build_dir),
+        "--output",
+        rebase_path(invoker.output, root_build_dir),
+      ]
+
+      if (_direct_deps_only) {
+        if (_use_interface_jars) {
           args += [
-            "--proguard-config-exclusions=$_rebased_proguard_config_exclusions",
+            "--jars=@FileArg($_rebased_build_config:javac:interface_classpath)",
           ]
-        }
-      }
-    } else {
-      action_with_pydeps(_jar_target_name) {
-        forward_variables_from(invoker, [ "data" ])
-        script = "//build/android/gyp/create_dist_jar.py"
-        depfile = "$target_gen_dir/$target_name.d"
-        deps = _deps
-
-        inputs = [
-          _build_config,
-        ]
-
-        outputs = [
-          invoker.output,
-        ]
-
-        args = [
-          "--depfile",
-          rebase_path(depfile, root_build_dir),
-          "--output",
-          rebase_path(invoker.output, root_build_dir),
-        ]
-
-        if (_direct_deps_only) {
-          if (_use_interface_jars) {
-            args += [ "--jars=@FileArg($_rebased_build_config:javac:interface_classpath)" ]
-          } else if (_use_unprocessed_jars) {
-            args +=
-                [ "--jars=@FileArg($_rebased_build_config:javac:classpath)" ]
-          } else {
-            assert(
-                false,
-                "direct_deps_only does not work without use_interface_jars or use_unprocessed_jars")
-          }
+        } else if (_use_unprocessed_jars) {
+          args += [ "--jars=@FileArg($_rebased_build_config:javac:classpath)" ]
         } else {
-          if (_use_interface_jars) {
-            args += [ "--jars=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)" ]
-          } else if (_use_unprocessed_jars) {
-            args += [ "--jars=@FileArg($_rebased_build_config:deps_info:javac_full_classpath)" ]
-          } else {
-            args += [ "--jars=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)" ]
-          }
+          assert(
+              false,
+              "direct_deps_only does not work without use_interface_jars or use_unprocessed_jars")
+        }
+      } else {
+        if (_use_interface_jars) {
+          args += [ "--jars=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)" ]
+        } else if (_use_unprocessed_jars) {
+          args += [ "--jars=@FileArg($_rebased_build_config:deps_info:javac_full_classpath)" ]
+        } else {
+          args += [ "--jars=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)" ]
         }
       }
     }
-    if (defined(invoker.dex_path)) {
-      dex(target_name) {
-        deps = [
-          ":$_jar_target_name",
-        ]
-        input_jars = [ invoker.output ]
-        output = invoker.dex_path
-      }
+  }
+
+  # Combines all dependent .jar files into a single proguarded .dex file.
+  #
+  # Variables:
+  #   output: Path to the output dex.
+  #   proguard_configs: List of proguard configs.
+  #   proguard_jar_path: The path to proguard.jar you wish to use. If undefined,
+  #     the proguard used will be the checked in one in //third_party/proguard.
+  #
+  # Example
+  #   dist_dex("lib_fatjar") {
+  #     deps = [ ":my_java_lib" ]
+  #     output = "$root_build_dir/MyLibrary.jar"
+  #   }
+  #   dist_jar("sideloaded_dex") {
+  #     deps = [ ":my_java_lib" ]
+  #     output = "$root_build_dir/MyLibrary.jar"
+  #     dex_path = "$root_build_dir/MyLibrary.dex"
+  #   }
+  template("proguarded_dist_dex") {
+    _deps = [
+      "//third_party/android_tools:android_sdk_java",
+      "//build/android/buildhooks:build_hooks_android_impl_java",
+    ]
+    if (defined(invoker.deps)) {
+      _deps += invoker.deps
+    }
+
+    _build_config = "$target_gen_dir/$target_name.build_config"
+    _build_config_target_name = "${target_name}__build_config"
+
+    write_build_config(_build_config_target_name) {
+      type = "dist_jar"
+      forward_variables_from(invoker, [ "proguard_configs" ])
+      supports_android = true
+      requires_android = true
+      proguard_enabled = true
+      possible_config_deps = _deps
+      build_config = _build_config
+    }
+
+    _deps += [ ":$_build_config_target_name" ]
+
+    dex(target_name) {
+      deps = _deps
+      build_config = _build_config
+      proguard_enabled = true
+      forward_variables_from(invoker, [ "proguard_configs" ])
+      output = invoker.output
     }
   }
 
@@ -1923,11 +1909,7 @@
 
     _enable_multidex =
         defined(invoker.enable_multidex) && invoker.enable_multidex
-    if (_enable_multidex) {
-      _final_dex_path = "$_gen_dir/classes.dex.zip"
-    } else {
-      _final_dex_path = "$_gen_dir/classes.dex"
-    }
+    _final_dex_path = "$_gen_dir/classes.dex.zip"
 
     if (defined(invoker.final_apk_path)) {
       _final_apk_path = invoker.final_apk_path
@@ -2100,7 +2082,7 @@
     _proguard_enabled =
         defined(invoker.proguard_enabled) && invoker.proguard_enabled
     if (_proguard_enabled) {
-      _proguard_output_jar_path = "$_base_path.proguard.jar"
+      _proguard_mapping_path = "$_final_apk_path.mapping"
     }
 
     # TODO(crbug.com/864142): Allow incremental installs of bundle modules.
@@ -2403,7 +2385,7 @@
         if (_enable_multidex) {
           proguard_configs += [ "//build/android/multidex.flags" ]
         }
-        proguard_output_jar_path = _proguard_output_jar_path
+        proguard_mapping_path = _proguard_mapping_path
       }
 
       # Don't depend on the runtime_deps target in order to avoid having to
@@ -2446,66 +2428,42 @@
     # place later due to synchronized proguarding. For more details,
     # read build/android/docs/android_app_bundles.md
     if (!(_is_bundle_module && _proguard_enabled)) {
-      if (_proguard_enabled) {
-        _proguard_target = "${_template_name}__proguard"
-        proguard(_proguard_target) {
-          forward_variables_from(invoker, [ "proguard_jar_path" ])
-          build_config = _build_config
-          deps = _deps + [
-                   ":$_build_config_target",
-                   ":$_compile_resources_target",
-                   ":$_java_target",
-                 ]
-          inputs = [
-            _jar_path,
-          ]
-
-          output_jar_path = _proguard_output_jar_path
-          args = [
-            "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
-            "--input-paths=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)",
-          ]
-          if (defined(invoker.proguard_config_exclusions)) {
-            _rebased_proguard_config_exclusions =
-                rebase_path(invoker.proguard_config_exclusions, root_build_dir)
-            args += [ "--proguard-config-exclusions=$_rebased_proguard_config_exclusions" ]
-          }
-          if (defined(invoker.apk_under_test)) {
-            args += [ "--mapping=@FileArg($_rebased_build_config:deps_info:proguard_under_test_mapping)" ]
-            deps += [ "${invoker.apk_under_test}__proguard" ]
-          }
-        }
-        _dex_sources = [ _proguard_output_jar_path ]
-        _dex_deps = [ ":$_proguard_target" ]
-
-        _copy_proguard_mapping_target =
-            "${_template_name}__copy_proguard_mapping"
-        copy(_copy_proguard_mapping_target) {
-          sources = [
-            "$_proguard_output_jar_path.mapping",
-          ]
-          outputs = [
-            "$_final_apk_path.mapping",
-          ]
-          deps = [
-            ":$_proguard_target",
-          ]
-        }
-      } else {
-        if (_enable_multidex) {
-          # .jar already included in java_runtime_classpath.
-          _dex_sources = []
-        } else {
-          _dex_sources = [ _lib_dex_path ]
-        }
-        _dex_deps = [ ":$_java_target" ]
-      }
-
       _final_dex_target_name = "${_template_name}__final_dex"
-      dex("$_final_dex_target_name") {
-        forward_variables_from(invoker, [ "min_sdk_version" ])
-        deps = _dex_deps + [ ":$_build_config_target" ]
-        input_jars = _dex_sources
+      dex(_final_dex_target_name) {
+        forward_variables_from(invoker,
+                               [
+                                 "min_sdk_version",
+                                 "dexlayout_profile",
+                               ])
+        proguard_enabled = _proguard_enabled
+        build_config = _build_config
+        deps = [
+          ":$_build_config_target",
+          ":$_java_target",
+        ]
+        if (_proguard_enabled) {
+          forward_variables_from(invoker, [ "proguard_jar_path" ])
+          deps += _deps + [ ":$_compile_resources_target" ]
+          proguard_configs = [ _jar_path ]
+          if (defined(invoker.apk_under_test)) {
+            proguard_args = [ "--mapping=@FileArg($_rebased_build_config:deps_info:proguard_under_test_mapping)" ]
+            deps += [ "${invoker.apk_under_test}__final_dex" ]
+          }
+          proguard_mapping_path = _proguard_mapping_path
+        } else {
+          if (_enable_multidex) {
+            # .jar already included in java_runtime_classpath.
+            input_jars = []
+            _dex_arg_key =
+                "${_rebased_build_config}:deps_info:java_runtime_classpath"
+          } else {
+            input_jars = [ _lib_dex_path ]
+            _dex_arg_key =
+                "${_rebased_build_config}:final_dex:dependency_dex_files"
+          }
+          input_jars_file_arg = "@FileArg($_dex_arg_key)"
+        }
+
         output = _final_dex_path
         enable_multidex = _enable_multidex
 
@@ -2514,24 +2472,6 @@
           extra_main_dex_proguard_config = _generated_proguard_main_dex_config
           deps += [ ":$_compile_resources_target" ]
         }
-
-        forward_variables_from(invoker, [ "dexlayout_profile" ])
-
-        # All deps are already included in _dex_sources when proguard is used.
-        if (!_proguard_enabled) {
-          if (_enable_multidex) {
-            _dex_arg_key =
-                "${_rebased_build_config}:deps_info:java_runtime_classpath"
-          } else {
-            _dex_arg_key =
-                "${_rebased_build_config}:final_dex:dependency_dex_files"
-          }
-          build_config = _build_config
-          input_jars_file_arg = "@FileArg($_dex_arg_key)"
-        }
-
-        # http://crbug.com/725224. Fix for bots running out of memory.
-        use_pool = true
       }
     } else {
       # A small sanity check to help developers with a subtle point!
@@ -2792,13 +2732,6 @@
 
         # Generate apk related operations at runtime.
         public_deps += _apk_operations
-
-        # Make the proguard .mapping file easy to find by putting it beside the .apk.
-        if (_proguard_enabled && !_is_bundle_module) {
-          deps = [
-            ":$_copy_proguard_mapping_target",
-          ]
-        }
       }
     }
 
@@ -3827,7 +3760,7 @@
         build_config = _build_config
         deps = _module_targets + [ ":$_build_config_target" ]
 
-        output_jar_path = _proguard_output_jar_path
+        output_path = _proguard_output_jar_path
         args = [
           "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
           "--input-paths=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)",
@@ -3885,9 +3818,6 @@
           input_jars = [ _module_jar_path ]
           output = _module_final_dex_path
 
-          # http://crbug.com/725224. Fix for bots running out of memory.
-          use_pool = true
-
           if (_enable_multidex && _module.name == "base") {
             enable_multidex = _enable_multidex
             extra_main_dex_proguard_config =
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index 6b9e900..89dca4b 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -68,6 +68,9 @@
 }
 
 void SurfaceLayer::SetFallbackSurfaceId(const viz::SurfaceId& surface_id) {
+  // The fallback should never move backwards.
+  DCHECK(!surface_range_.start() ||
+         !surface_range_.start()->IsNewerThan(surface_id));
   if (surface_range_.start() == surface_id)
     return;
   TRACE_EVENT_WITH_FLOW2(
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 1279138c..d1c30fdd 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1501,7 +1501,9 @@
     }
 
     version_name = chrome_version_name
-    enable_multidex = true
+    if (experimental_r8_path == "") {
+      enable_multidex = true
+    }
   }
 }
 
@@ -1849,7 +1851,9 @@
   base_module_target = ":monochrome_public_base_module"
   if (!is_java_debug) {
     proguard_enabled = true
-    enable_multidex = true
+    if (experimental_r8_path == "") {
+      enable_multidex = true
+    }
     proguard_android_sdk_dep = webview_framework_dep
   }
   if (modularize_ar) {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 2d7ed73..e97b534 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -244,12 +244,9 @@
     @Override
     protected void initializeMainView(Context context) {
         int topPadding = context.getResources().getDimensionPixelOffset(R.dimen.tab_strip_height);
-        int bottomPadding = mTab.getActivity().getFullscreenManager().getBottomControlsHeight();
 
         mRootView = new RootView(context);
-        mRootView.setLayoutParams(new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
-        mRootView.setPadding(0, topPadding, 0, bottomPadding);
+        mRootView.setPadding(0, topPadding, 0, 0);
         mUiConfig = new UiConfig(mRootView);
     }
 
diff --git a/chrome/android/java/res/drawable/ic_article_blue_24dp.xml b/chrome/android/java/res/drawable/ic_article_blue_24dp.xml
new file mode 100644
index 0000000..223976ff
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_article_blue_24dp.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<vector xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21"
+    android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/default_icon_color_blue" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_restaurant_menu_blue_24dp.xml b/chrome/android/java/res/drawable/ic_restaurant_menu_blue_24dp.xml
deleted file mode 100644
index 681c872..0000000
--- a/chrome/android/java/res/drawable/ic_restaurant_menu_blue_24dp.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<vector xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="24dp" android:width="24dp"
-    android:viewportHeight="24.0" android:viewportWidth="24.0">
-    <path android:fillColor="@color/default_icon_color_blue" android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zM14.88,11.53c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
-</vector>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 8ec885a..2bccbc0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1440,6 +1440,8 @@
             mManualFillingController.initialize(getWindowAndroid(),
                     new DeferredViewStubInflationProvider<>(accessoryBarStub),
                     new DeferredViewStubInflationProvider<>(accessorySheetStub));
+            getCompositorViewHolder().setKeyboardExtensionView(
+                    mManualFillingController.getKeyboardExtensionSizeManager());
         }
 
         // Create after native initialization so subclasses that override this method have a chance
@@ -2022,10 +2024,7 @@
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
-        // The conditions are expressed using ranges to capture intermediate levels possibly added
-        // to the API in the future.
-        if ((level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
-                || level >= TRIM_MEMORY_MODERATE) {
+        if (ChromeApplication.isSevereMemorySignal(level)) {
             mReferencePool.drain();
             clearToolbarResourceCache();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index ddf0450..91185f2b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -137,14 +137,19 @@
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
+        if (isSevereMemorySignal(level) && mReferencePool != null) mReferencePool.drain();
+        CustomTabsConnection.onTrimMemory(level);
+    }
+
+    /**
+     * Determines whether the given memory signal is considered severe.
+     * @param level The type of signal as defined in {@link android.content.ComponentCallbacks2}.
+     */
+    public static boolean isSevereMemorySignal(int level) {
         // The conditions are expressed using ranges to capture intermediate levels possibly added
         // to the API in the future.
-        if ((level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
-                || level >= TRIM_MEMORY_MODERATE) {
-            if (mReferencePool != null) mReferencePool.drain();
-
-            CustomTabsConnection.trimMemory();
-        }
+        return (level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
+                || level >= TRIM_MEMORY_MODERATE;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index d23df88..15ea63a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -147,6 +147,8 @@
     }
 
     // Alphabetical:
+    public static final String AUTOFILL_ALLOW_NON_HTTP_ACTIVATION =
+            "AutofillAllowNonHttpActivation";
     public static final String ADJUST_WEBAPK_INSTALLATION_SPACE = "AdjustWebApkInstallationSpace";
     public static final String ANDROID_PAY_INTEGRATION_V1 = "AndroidPayIntegrationV1";
     public static final String ANDROID_PAY_INTEGRATION_V2 = "AndroidPayIntegrationV2";
@@ -167,6 +169,8 @@
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
     public static final String CCT_REDIRECT_PRECONNECT = "CCTRedirectPreconnect";
     public static final String CCT_RESOURCE_PREFETCH = "CCTResourcePrefetch";
+    public static final String CCT_REPORT_PARALLEL_REQUEST_STATUS =
+            "CCTReportParallelRequestStatus";
     public static final String CHROME_DUET = "ChromeDuet";
     // TODO(mdjones): Remove CHROME_HOME_SWIPE_VELOCITY_FEATURE or rename.
     public static final String CHROME_HOME_SWIPE_VELOCITY_FEATURE = "ChromeHomeSwipeLogicVelocity";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index aa249f3..0fdadf9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2027,10 +2027,7 @@
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
-        // The conditions are expressed using ranges to capture intermediate levels possibly added
-        // to the API in the future.
-        if ((level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
-                || level >= TRIM_MEMORY_MODERATE) {
+        if (ChromeApplication.isSevereMemorySignal(level)) {
             NativePageAssassin.getInstance().freezeAllHiddenPages();
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java
new file mode 100644
index 0000000..a9d2d08c
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.support.annotation.Px;
+
+import org.chromium.base.ObserverList;
+
+/**
+ * This class holds the size of any extension to or even replacement for a keyboard.
+ * For example, it is used by {@link ManualFillingCoordinator} to provide the combined height of
+ * {@link KeyboardAccessoryCoordinator} and {@link AccessorySheetCoordinator}.
+ * The height can then be used to either compute an offset for bottom bars (e.g. CCTs or PWAs) or to
+ * push up the content area.
+ */
+public class KeyboardExtensionSizeManager {
+    private int mHeight;
+    private final ObserverList<Observer> mObservers = new ObserverList<>();
+
+    /**
+     * Observers are notified when the size of the keyboard extension changes.
+     */
+    public interface Observer {
+        /**
+         * Called when the extension height changes.
+         * @param keyboardHeight The new height of the keyboard extension.
+         */
+        void onKeyboardExtensionHeightChanged(@Px int keyboardHeight);
+    }
+
+    /**
+     * Returns the height of the keyboard extension.
+     * @return A height in pixels.
+     */
+    public @Px int getKeyboardExtensionHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Registered observers are called whenever the extension size changes until unregistered. Does
+     * not guarantee order.
+     * @param observer a {@link KeyboardExtensionSizeManager.Observer}.
+     */
+    public void addObserver(Observer observer) {
+        mObservers.addObserver(observer);
+    }
+
+    /**
+     * Removes a registered observer if present.
+     * @param observer a registered {@link KeyboardExtensionSizeManager.Observer}.
+     */
+    public void removeObserver(Observer observer) {
+        mObservers.removeObserver(observer);
+    }
+
+    /**
+     * Sets a new extension height and notifies observers if its value changed.
+     * @param newKeyboardExtensionHeight The height in pixels.
+     */
+    void setKeyboardExtensionHeight(@Px int newKeyboardExtensionHeight) {
+        if (mHeight == newKeyboardExtensionHeight) return;
+        mHeight = newKeyboardExtensionHeight;
+        for (Observer observer : mObservers) observer.onKeyboardExtensionHeightChanged(mHeight);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
index c97d6818..300f5db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
@@ -99,6 +99,14 @@
         mMediator.registerPasswordProvider(itemProvider);
     }
 
+    public void showWhenKeyboardIsVisible() {
+        mMediator.showWhenKeyboardIsVisible();
+    }
+
+    public void hide() {
+        mMediator.hide();
+    }
+
     public void onResume() {
         mMediator.resume();
     }
@@ -107,6 +115,16 @@
         mMediator.pause();
     }
 
+    /**
+     * Returns a size manager that allows to access the combined height of
+     * {@link KeyboardAccessoryCoordinator} and {@link AccessorySheetCoordinator}, and to be
+     * notified when it changes.
+     * @return A {@link KeyboardExtensionSizeManager}.
+     */
+    public KeyboardExtensionSizeManager getKeyboardExtensionSizeManager() {
+        return mMediator.getKeyboardExtensionSizeManager();
+    }
+
     // TODO(fhorschig): Should be @VisibleForTesting.
     /**
      * Allows access to the keyboard accessory. This can be used to explicitly modify the the bar of
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
index 3be9b62..32e289d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
-import org.chromium.chrome.browser.webapps.WebappActivity;
 import org.chromium.ui.DropdownPopupWindow;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -44,10 +43,12 @@
 class ManualFillingMediator
         extends EmptyTabObserver implements KeyboardAccessoryCoordinator.VisibilityDelegate {
     private WindowAndroid mWindowAndroid;
-    private @Px int mPreviousControlHeight;
     private final WindowAndroid.KeyboardVisibilityListener mVisibilityListener =
             this::onKeyboardVisibilityChanged;
     private Supplier<InsetObserverView> mInsetObserverViewSupplier;
+    private boolean mShouldShow = false;
+    private final KeyboardExtensionSizeManager mKeyboardExtensionSizeManager =
+            new KeyboardExtensionSizeManager();
 
     /**
      * Provides a cache for a given Provider which can repeat the last notification to all
@@ -146,8 +147,6 @@
     void initialize(KeyboardAccessoryCoordinator keyboardAccessory,
             AccessorySheetCoordinator accessorySheet, WindowAndroid windowAndroid) {
         assert windowAndroid.getActivity().get() != null;
-        // Abort initialization for PWAs: https://crbug.com/881536.
-        if (windowAndroid.getActivity().get() instanceof WebappActivity) return;
         mWindowAndroid = windowAndroid;
         mKeyboardAccessory = keyboardAccessory;
         mAccessorySheet = accessorySheet;
@@ -160,7 +159,6 @@
             @Override
             public void didSelectTab(Tab tab, @TabModel.TabSelectionType int type, int lastId) {
                 mActiveBrowserTab = tab;
-                mPreviousControlHeight = mActivity.getFullscreenManager().getBottomControlsHeight();
                 restoreCachedState(tab);
             }
 
@@ -193,18 +191,9 @@
     void onKeyboardVisibilityChanged(boolean isShowing) {
         if (!mKeyboardAccessory.hasContents()) return; // Exit early to not affect the layout.
         if (isShowing) {
-            // Don't open the accessory inside the contextual search panel.
-            ContextualSearchManager contextualSearchManager =
-                    mActivity.getContextualSearchManager();
-            if (contextualSearchManager != null && contextualSearchManager.isSearchPanelOpened()) {
-                return;
+            if (mShouldShow) {
+                displayKeyboardAccessory();
             }
-            mKeyboardAccessory.requestShowing();
-            mActivity.getFullscreenManager().setBottomControlsHeight(calculateAccessoryBarHeight());
-            mKeyboardAccessory.closeActiveTab();
-            updateInfobarState(true);
-            mKeyboardAccessory.setBottomOffset(0);
-            mAccessorySheet.hide();
         } else {
             mKeyboardAccessory.close();
             onBottomControlSpaceChanged();
@@ -262,6 +251,20 @@
         mPopup = popup;
     }
 
+    void showWhenKeyboardIsVisible() {
+        if (!isInitialized() || !mKeyboardAccessory.hasContents() || mShouldShow) return;
+        mShouldShow = true;
+        ViewGroup contentView = getContentView();
+        if (mWindowAndroid.getKeyboardDelegate().isKeyboardShowing(mActivity, contentView)) {
+            displayKeyboardAccessory();
+        }
+    }
+
+    void hide() {
+        mShouldShow = false;
+        pause();
+    }
+
     public void pause() {
         if (!isInitialized()) return;
         mKeyboardAccessory.dismiss();
@@ -273,6 +276,24 @@
         restoreCachedState(mActiveBrowserTab);
     }
 
+    private void displayKeyboardAccessory() {
+        // Don't open the accessory inside the contextual search panel.
+        ContextualSearchManager contextualSearchManager = mActivity.getContextualSearchManager();
+        if (contextualSearchManager != null && contextualSearchManager.isSearchPanelOpened()) {
+            return;
+        }
+        mKeyboardAccessory.requestShowing();
+        mKeyboardExtensionSizeManager.setKeyboardExtensionHeight(calculateAccessoryBarHeight());
+        mKeyboardAccessory.closeActiveTab();
+        updateInfobarState(true);
+        mKeyboardAccessory.setBottomOffset(0);
+        mAccessorySheet.hide();
+    }
+
+    KeyboardExtensionSizeManager getKeyboardExtensionSizeManager() {
+        return mKeyboardExtensionSizeManager;
+    }
+
     @Override
     public void onChangeAccessorySheet(int tabIndex) {
         assert mActivity != null : "ManualFillingMediator needs initialization.";
@@ -294,7 +315,7 @@
         if (mWindowAndroid.getKeyboardDelegate().isKeyboardShowing(mActivity, contentView)) {
             return; // If the keyboard is showing or is starting to show, the sheet closes gently.
         }
-        mActivity.getFullscreenManager().setBottomControlsHeight(mPreviousControlHeight);
+        mKeyboardExtensionSizeManager.setKeyboardExtensionHeight(0);
         mKeyboardAccessory.closeActiveTab();
         updateInfobarState(false);
         mKeyboardAccessory.setBottomOffset(0);
@@ -311,7 +332,7 @@
     @Override
     public void onOpenKeyboard() {
         assert mActivity != null : "ManualFillingMediator needs initialization.";
-        mActivity.getFullscreenManager().setBottomControlsHeight(calculateAccessoryBarHeight());
+        mKeyboardExtensionSizeManager.setKeyboardExtensionHeight(calculateAccessoryBarHeight());
         if (mActivity.getCurrentFocus() != null) {
             mWindowAndroid.getKeyboardDelegate().showKeyboard(mActivity.getCurrentFocus());
         }
@@ -326,8 +347,9 @@
             newControlsOffset += mAccessorySheet.getHeight();
         }
         mKeyboardAccessory.setBottomOffset(newControlsOffset);
-        mActivity.getFullscreenManager().setBottomControlsHeight(
-                mKeyboardAccessory.isShown() ? newControlsHeight : mPreviousControlHeight);
+        mKeyboardExtensionSizeManager.setKeyboardExtensionHeight(
+                mKeyboardAccessory.isShown() ? newControlsHeight : 0);
+        mActivity.getFullscreenManager().updateViewportSize();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
index 145fb92..b9ddbce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
@@ -71,6 +71,16 @@
     }
 
     @CalledByNative
+    void showWhenKeyboardIsVisible() {
+        mManualFillingCoordinator.showWhenKeyboardIsVisible();
+    }
+
+    @CalledByNative
+    void hide() {
+        mManualFillingCoordinator.hide();
+    }
+
+    @CalledByNative
     private void closeAccessorySheet() {
         mManualFillingCoordinator.closeAccessorySheet();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index 8e7e2bb..f9e7432 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
-import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
@@ -23,8 +22,11 @@
 
 import org.chromium.base.Promise;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.profiles.Profile;
 
 import java.io.InputStream;
 import java.net.URL;
@@ -36,13 +38,16 @@
 
 /** Delegate to interact with the assistant UI. */
 class AutofillAssistantUiDelegate {
+    private static final String FEEDBACK_CATEGORY_TAG =
+            "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
+
     // TODO(crbug.com/806868): Use correct user locale.
     private static final SimpleDateFormat sDetailsTimeFormat =
             new SimpleDateFormat("H:mma", Locale.getDefault());
     private static final SimpleDateFormat sDetailsDateFormat =
             new SimpleDateFormat("EEE, MMM d", Locale.getDefault());
 
-    private final Activity mActivity;
+    private final ChromeActivity mActivity;
     private final Client mClient;
     private final View mFullContainer;
     private final View mOverlay;
@@ -160,10 +165,10 @@
     /**
      * Constructs an assistant UI delegate.
      *
-     * @param activity The Activity
+     * @param activity The ChromeActivity
      * @param client The client to forward events to
      */
-    public AutofillAssistantUiDelegate(Activity activity, Client client) {
+    public AutofillAssistantUiDelegate(ChromeActivity activity, Client client) {
         mActivity = activity;
         mClient = client;
 
@@ -178,9 +183,9 @@
         mBottomBar.findViewById(R.id.close_button).setOnClickListener(unusedView -> shutdown());
         mBottomBar.findViewById(R.id.feedback_button)
                 .setOnClickListener(unusedView
-                        -> {
-                                // TODO(crbug.com/806868): Send feedback.
-                        });
+                        -> HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity,
+                                Profile.getLastUsedProfile(), mActivity.getActivityTab().getUrl(),
+                                FEEDBACK_CATEGORY_TAG));
         mChipsViewContainer = mBottomBar.findViewById(R.id.carousel);
         mStatusMessageView = mBottomBar.findViewById(R.id.status_message);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 8b32f05..216df02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -14,6 +14,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.annotation.Px;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -32,6 +34,7 @@
 import org.chromium.base.TraceEvent;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.InsetObserverView;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardExtensionSizeManager;
 import org.chromium.chrome.browser.compositor.Invalidator.Client;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerHost;
@@ -72,7 +75,8 @@
  */
 public class CompositorViewHolder extends FrameLayout
         implements ContentOffsetProvider, LayoutManagerHost, LayoutRenderHost, Invalidator.Host,
-                   FullscreenListener, InsetObserverView.WindowInsetObserver {
+                   FullscreenListener, InsetObserverView.WindowInsetObserver,
+                   KeyboardExtensionSizeManager.Observer {
     private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500;
 
     private boolean mIsKeyboardShowing;
@@ -103,6 +107,7 @@
     /** The toolbar control container. **/
     private ControlContainer mControlContainer;
 
+    private @Nullable KeyboardExtensionSizeManager mKeyboardExtensionSizeManager;
     private InsetObserverView mInsetObserverView;
     private boolean mShowingFullscreen;
     private Runnable mSystemUiFullscreenResizeRunnable;
@@ -374,6 +379,38 @@
     public void onSafeAreaChanged(Rect area) {}
 
     /**
+     * Allows to set (or unset if called with null) the {@link KeyboardExtensionSizeManager} that
+     * provides the dimensions of any keyboard extensions or replacements. Registers an observer to
+     * react to size changes immediately.
+     * @param manager A {@link KeyboardExtensionSizeManager}. Optional.
+     */
+    public void setKeyboardExtensionView(@Nullable KeyboardExtensionSizeManager manager) {
+        if (mKeyboardExtensionSizeManager != null) {
+            mKeyboardExtensionSizeManager.removeObserver(this);
+        }
+        mKeyboardExtensionSizeManager = manager;
+        if (mKeyboardExtensionSizeManager != null) {
+            mKeyboardExtensionSizeManager.addObserver(this);
+            onViewportChanged();
+        }
+    }
+
+    @Override
+    public void onKeyboardExtensionHeightChanged(int keyboardHeight) {
+        onUpdateViewportSize();
+    }
+
+    /**
+     * Returns the combined height of all extensions to or replacements of the keyboard which
+     * consume space at the bottom of the content area.
+     * @return the full height in pixels.
+     */
+    public @Px int getKeyboardExtensionsHeight() {
+        if (mKeyboardExtensionSizeManager == null) return 0;
+        return mKeyboardExtensionSizeManager.getKeyboardExtensionHeight();
+    }
+
+    /**
      * Should be called for cleanup when the CompositorView instance is no longer used.
      */
     public void shutDown() {
@@ -572,6 +609,7 @@
         int controlsHeight = controlsResizeView()
                 ? getTopControlsHeightPixels() + getBottomControlsHeightPixels()
                 : 0;
+        controlsHeight += getKeyboardExtensionsHeight();
         if (isAttachedToWindow(view)) {
             webContents.setSize(w, h - controlsHeight);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index 2e84b3cb..30fee587 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -338,7 +338,7 @@
      * @param contacts The contacts that were selected (if any).
      */
     private void executeAction(
-            ContactsPickerListener.ContactsPickerAction action, String contacts) {
+            @ContactsPickerListener.ContactsPickerAction int action, String contacts) {
         mListener.onContactsPickerUserAction(action, contacts);
         mDialog.dismiss();
         UiUtils.onContactsPickerDismissed();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 22939f1..9de3cae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -1009,7 +1009,7 @@
             moduleComponentName = mIntentDataProvider.getModuleComponentName();
         }
         if (moduleComponentName != null) {
-            mConnection.getModuleLoader(moduleComponentName).maybeUnloadModule();
+            mConnection.getModuleLoader(moduleComponentName).decrementModuleUseCount();
         }
         if (mIncognitoTabHost != null) {
             IncognitoTabHostRegistry.getInstance().unregister(mIncognitoTabHost);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
index 72e8199..1b000251 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -334,6 +334,24 @@
     }
 
     @Override
+    public void onUpdateViewportSize() {
+        if (mBottomBarView == null) return; // Check bottom bar view but don't inflate it.
+        // Hide the container of the bottom bar while the extension is showing. This doesn't
+        // affect the content.
+        boolean keyboardExtensionHidesBottomBar = mActivity.getManualFillingController()
+                                                          .getKeyboardExtensionSizeManager()
+                                                          .getKeyboardExtensionHeight()
+                > 0;
+        if (keyboardExtensionHidesBottomBar) {
+            getBottomBarView().setVisibility(View.GONE);
+            mFullscreenManager.setBottomControlsHeight(0);
+        } else {
+            getBottomBarView().setVisibility(View.VISIBLE);
+            mFullscreenManager.setBottomControlsHeight(getBottomBarHeight());
+        }
+    }
+
+    @Override
     public void onContentOffsetChanged(float offset) { }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index f31fedc..ff25d22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -37,6 +37,7 @@
 import org.chromium.base.TimeUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
@@ -96,6 +97,7 @@
     private static final String TAG = "ChromeConnection";
     private static final String LOG_SERVICE_REQUESTS = "custom-tabs-log-service-requests";
 
+    // Callback names for |extraCallback()|.
     @VisibleForTesting
     static final String PAGE_LOAD_METRICS_CALLBACK = "NavigationMetrics";
     static final String BOTTOM_BAR_SCROLL_STATE_CALLBACK = "onBottomBarScrollStateChanged";
@@ -103,6 +105,10 @@
     static final String OPEN_IN_BROWSER_CALLBACK = "onOpenInBrowser";
     @VisibleForTesting
     static final String ON_WARMUP_COMPLETED = "onWarmupCompleted";
+    @VisibleForTesting
+    static final String ON_DETACHED_REQUEST_REQUESTED = "onDetachedRequestRequested";
+    @VisibleForTesting
+    static final String ON_DETACHED_REQUEST_COMPLETED = "onDetachedRequestCompleted";
 
     // For CustomTabs.SpeculationStatusOnStart, see tools/metrics/enums.xml. Append only.
     private static final int SPECULATION_STATUS_ON_START_ALLOWED = 0;
@@ -876,6 +882,19 @@
         if (mLogRequests) {
             Log.w(TAG, "handleParallelRequest() = " + PARALLEL_REQUEST_MESSAGES[status]);
         }
+
+        if ((status != ParallelRequestStatus.NO_REQUEST)
+                && (status != ParallelRequestStatus.FAILURE_NOT_INITIALIZED)
+                && (status != ParallelRequestStatus.FAILURE_NOT_AUTHORIZED)
+                && ChromeFeatureList.isEnabled(
+                           ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)) {
+            Bundle args = new Bundle();
+            Uri url = intent.getParcelableExtra(PARALLEL_REQUEST_URL_KEY);
+            args.putParcelable("url", url);
+            args.putInt("status", status);
+            safeExtraCallback(session, ON_DETACHED_REQUEST_REQUESTED, args);
+        }
+
         return status;
     }
 
@@ -914,11 +933,13 @@
 
         String urlString = url.toString();
         String referrerString = referrer.toString();
-        nativeCreateAndStartDetachedResourceRequest(Profile.getLastUsedProfile(), urlString,
-                referrerString, policy, DetachedResourceRequestMotivation.PARALLEL_REQUEST);
+        nativeCreateAndStartDetachedResourceRequest(Profile.getLastUsedProfile(), session,
+                urlString, referrerString, policy,
+                DetachedResourceRequestMotivation.PARALLEL_REQUEST);
         if (mLogRequests) {
             Log.w(TAG, "startParallelRequest(%s, %s, %d)", urlString, referrerString, policy);
         }
+
         return ParallelRequestStatus.SUCCESS;
     }
 
@@ -951,8 +972,10 @@
             String urlString = url.toString();
             if (urlString.isEmpty() || !isValid(url)) continue;
 
-            nativeCreateAndStartDetachedResourceRequest(Profile.getLastUsedProfile(), urlString,
-                    referrerString, policy, DetachedResourceRequestMotivation.RESOURCE_PREFETCH);
+            // Session is null because we don't need completion notifications.
+            nativeCreateAndStartDetachedResourceRequest(Profile.getLastUsedProfile(), null,
+                    urlString, referrerString, policy,
+                    DetachedResourceRequestMotivation.RESOURCE_PREFETCH);
             ++requestsSent;
 
             if (mLogRequests) {
@@ -1333,11 +1356,15 @@
 
     /**
      * Discards substantial objects that are not currently in use.
+     * @param level The type of signal as defined in {@link android.content.ComponentCallbacks2}.
      */
-    public static void trimMemory() {
+    public static void onTrimMemory(int level) {
         if (!hasInstance()) return;
-        getInstance().mClientManager.cleanupUnusedSessions();
-        if (getInstance().mModuleLoader != null) getInstance().mModuleLoader.trimMemory();
+
+        if (ChromeApplication.isSevereMemorySignal(level)) {
+            getInstance().mClientManager.cleanupUnusedSessions();
+        }
+        if (getInstance().mModuleLoader != null) getInstance().mModuleLoader.onTrimMemory(level);
     }
 
     @VisibleForTesting
@@ -1485,7 +1512,8 @@
     }
 
     private static native void nativeCreateAndStartDetachedResourceRequest(Profile profile,
-            String url, String origin, @WebReferrerPolicy int referrerPolicy,
+            CustomTabsSessionToken session, String url, String origin,
+            @WebReferrerPolicy int referrerPolicy,
             @DetachedResourceRequestMotivation int motivation);
 
     public ModuleLoader getModuleLoader(ComponentName componentName) {
@@ -1502,4 +1530,16 @@
             ActivityDelegate activityDelegate, int moduleVersion) {
         mClientManager.setActivityDelegateForSession(sessionToken, activityDelegate, moduleVersion);
     }
+
+    @CalledByNative
+    public static void notifyClientOfDetachedRequestCompletion(
+            CustomTabsSessionToken session, String url, int status) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)) {
+            return;
+        }
+        Bundle args = new Bundle();
+        args.putParcelable("url", Uri.parse(url));
+        args.putInt("net_error", status);
+        getInstance().safeExtraCallback(session, ON_DETACHED_REQUEST_COMPLETED, args);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index afcf3f725..e829ece 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -16,9 +17,11 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.task.AsyncTask;
+import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.crash.CrashKeyIndex;
 import org.chromium.chrome.browser.crash.CrashKeys;
+import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics.DestructionReason;
 
 /**
  * Dynamically loads a module from another apk.
@@ -29,7 +32,30 @@
     /** Specifies the module package name and entry point class name. */
     private final ComponentName mComponentName;
     private final String mModuleId;
+
+    /**
+     * Tracks the number of usages of the module. If it is no longer used, it may be destroyed, but
+     * the time of destruction depends on the caching policy.
+     */
     private int mModuleUseCount;
+
+    /**
+     * The timestamp of the moment the module became unused. This is used to determine whether or
+     * not to continue caching it. A value of -1 indicates there is no usable value.
+     */
+    private long mModuleUnusedTimeMs = -1;
+
+    /**
+     * The name of the experiment parameter for setting the caching time limit.
+     */
+    private static final String MODULE_CACHE_TIME_LIMIT_MS_NAME = "cct_module_cache_time_limit_ms";
+
+    /**
+     * The default time limit for caching an unused module under mild memory pressure, in
+     * milliseconds.
+     */
+    private static final int MODULE_CACHE_TIME_LIMIT_MS_DEFAULT = 300000; // 5 minutes
+
     @Nullable
     private ModuleEntryPoint mModuleEntryPoint;
 
@@ -75,6 +101,7 @@
     public Runnable loadModule(Callback<ModuleEntryPoint> callback) {
         if (mModuleEntryPoint != null) {
             mModuleUseCount++;
+            mModuleUnusedTimeMs = -1;
             ModuleMetrics.recordLoadResult(ModuleMetrics.LoadResult.SUCCESS_CACHED);
             callback.onResult(mModuleEntryPoint);
             return null;
@@ -97,25 +124,52 @@
         };
     }
 
-    public void maybeUnloadModule() {
+    public void decrementModuleUseCount() {
         if (mModuleEntryPoint == null) return;
         mModuleUseCount--;
-        if (mModuleUseCount == 0
-                && !ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_MODULE_CACHE)) {
-            destroyModule();
+        if (mModuleUseCount == 0) {
+            mModuleUnusedTimeMs = ModuleMetrics.now();
+            if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_MODULE_CACHE)) {
+                destroyModule(DestructionReason.NO_CACHING_UNUSED);
+            }
         }
     }
 
-    public void trimMemory() {
+    /**
+     * Destroys the unused cached module (if present) under certain circumstances. If the memory
+     * signal is considered severe, the module will always be destroyed. If the memory signal is
+     * considered mild, the module will only be destroyed if the time limit has passed.
+     * @param level The type of signal as defined in {@link ComponentCallbacks2}.
+     */
+    public void onTrimMemory(int level) {
         if (mModuleEntryPoint == null || mModuleUseCount > 0) return;
-        destroyModule();
+
+        if (ChromeApplication.isSevereMemorySignal(level)) {
+            destroyModule(DestructionReason.CACHED_SEVERE_MEMORY_PRESSURE);
+        } else if (cacheExceededTimeLimit()) {
+            if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+                destroyModule(DestructionReason.CACHED_UI_HIDDEN_TIME_EXCEEDED);
+            } else {
+                destroyModule(DestructionReason.CACHED_MILD_MEMORY_PRESSURE_TIME_EXCEEDED);
+            }
+        }
     }
 
-    private void destroyModule() {
+    private boolean cacheExceededTimeLimit() {
+        if (mModuleUnusedTimeMs == -1) return false;
+        long limit = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                ChromeFeatureList.CCT_MODULE_CACHE, MODULE_CACHE_TIME_LIMIT_MS_NAME,
+                MODULE_CACHE_TIME_LIMIT_MS_DEFAULT);
+        return ModuleMetrics.now() - mModuleUnusedTimeMs > limit;
+    }
+
+    private void destroyModule(@DestructionReason int reason) {
         assert mModuleEntryPoint != null;
+        ModuleMetrics.recordDestruction(reason);
         mModuleEntryPoint.onDestroy();
         CrashKeys.getInstance().set(CrashKeyIndex.ACTIVE_DYNAMIC_MODULE, null);
         mModuleEntryPoint = null;
+        mModuleUnusedTimeMs = -1;
     }
 
     /**
@@ -200,6 +254,7 @@
                 ModuleMetrics.recordLoadResult(ModuleMetrics.LoadResult.SUCCESS_NEW);
                 mModuleEntryPoint = entryPoint;
                 mModuleUseCount = 1;
+                mModuleUnusedTimeMs = -1;
                 mCallback.onResult(entryPoint);
                 return;
             } catch (Exception e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java
index 06573fa0..199af3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java
@@ -67,6 +67,38 @@
     }
 
     /**
+     * Possible reasons for destroying a dynamic module. Keep in sync with the
+     * CustomTabs.DynamicModule.DestructionReason enum in histograms.xml. Do not remove
+     * or change existing values other than NUM_ENTRIES.
+     */
+    @IntDef({DestructionReason.NO_CACHING_UNUSED, DestructionReason.CACHED_SEVERE_MEMORY_PRESSURE,
+            DestructionReason.CACHED_MILD_MEMORY_PRESSURE_TIME_EXCEEDED,
+            DestructionReason.CACHED_UI_HIDDEN_TIME_EXCEEDED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DestructionReason {
+        /** No caching enabled and the module is unused. */
+        int NO_CACHING_UNUSED = 0;
+        /** Cached and severe memory pressure. */
+        int CACHED_SEVERE_MEMORY_PRESSURE = 1;
+        /** Cached and mild memory pressure and time exceeded. */
+        int CACHED_MILD_MEMORY_PRESSURE_TIME_EXCEEDED = 2;
+        /** Cached and app hidden and time exceeded. */
+        int CACHED_UI_HIDDEN_TIME_EXCEEDED = 3;
+        /** Upper bound for legal sample values - all sample values have to be strictly lower. */
+        int NUM_ENTRIES = 4;
+    }
+
+    /**
+     * Records the reason for destroying a dynamic module.
+     * @param reason reason key, one of {@link DestructionReason}'s values.
+     */
+    public static void recordDestruction(@DestructionReason int reason) {
+        RecordHistogram.recordEnumeratedHistogram("CustomTabs.DynamicModule.DestructionReason",
+                reason, DestructionReason.NUM_ENTRIES);
+        Log.d(TAG, "Destroyed module, reason: %s", reason);
+    }
+
+    /**
      * SystemClock.uptimeMillis() is used here as it uses the same system call as all the native
      * side of Chrome, and this is the same clock used for page load metrics.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
index 62537a44..92ca8998 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
@@ -78,11 +78,11 @@
     private List<ExploreSitesCategory> createDefaultCategoryTiles() {
         List<ExploreSitesCategory> categoryList = new ArrayList<>();
 
-        // Sport category.
+        // News category.
         ExploreSitesCategory category =
-                new ExploreSitesCategory(-1 /* category_id */, CategoryType.SPORT,
-                        getContext().getString(R.string.explore_sites_default_category_sports));
-        category.setDrawable(getVectorDrawable(R.drawable.ic_directions_run_blue_24dp));
+                new ExploreSitesCategory(-1 /* category_id */, CategoryType.NEWS,
+                        getContext().getString(R.string.explore_sites_default_category_news));
+        category.setDrawable(getVectorDrawable(R.drawable.ic_article_blue_24dp));
         categoryList.add(category);
 
         // Shopping category.
@@ -91,11 +91,12 @@
         category.setDrawable(getVectorDrawable(R.drawable.ic_shopping_basket_blue_24dp));
         categoryList.add(category);
 
-        // Food category.
-        category = new ExploreSitesCategory(-1 /* category_id */, CategoryType.FOOD,
-                getContext().getString(R.string.explore_sites_default_category_cooking));
-        category.setDrawable(getVectorDrawable(R.drawable.ic_restaurant_menu_blue_24dp));
+        // Sport category.
+        category = new ExploreSitesCategory(-1 /* category_id */, CategoryType.SPORT,
+                getContext().getString(R.string.explore_sites_default_category_sports));
+        category.setDrawable(getVectorDrawable(R.drawable.ic_directions_run_blue_24dp));
         categoryList.add(category);
+
         return categoryList;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
index cb9fc64..3f1359e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
@@ -18,12 +18,14 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid;
+import org.chromium.chrome.browser.tabmodel.TabWindowManager;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -43,6 +45,7 @@
     private static final String NOTIFICATION_NAMESPACE = "MediaCaptureNotificationService";
 
     private static final String NOTIFICATION_ID_EXTRA = "NotificationId";
+    private static final String NOTIFICATION_MEDIA_IS_INCOGNITO = "NotificationIsIncognito";
     private static final String NOTIFICATION_MEDIA_TYPE_EXTRA = "NotificationMediaType";
     private static final String NOTIFICATION_MEDIA_URL_EXTRA = "NotificationMediaUrl";
 
@@ -96,9 +99,10 @@
             int notificationId = intent.getIntExtra(NOTIFICATION_ID_EXTRA, Tab.INVALID_TAB_ID);
             int mediaType = intent.getIntExtra(NOTIFICATION_MEDIA_TYPE_EXTRA, MEDIATYPE_NO_MEDIA);
             String url = intent.getStringExtra(NOTIFICATION_MEDIA_URL_EXTRA);
+            boolean isIncognito = intent.getBooleanExtra(NOTIFICATION_MEDIA_IS_INCOGNITO, false);
 
             if (ACTION_MEDIA_CAPTURE_UPDATE.equals(action)) {
-                updateNotification(notificationId, mediaType, url);
+                updateNotification(notificationId, mediaType, url, isIncognito);
             } else if (ACTION_SCREEN_CAPTURE_STOP.equals(action)) {
                 // Notify native to stop screen capture when the STOP button in notification
                 // is clicked.
@@ -132,14 +136,15 @@
      * @param mediaType Media type of the notification.
      * @param url Url of the current webrtc call.
      */
-    private void updateNotification(int notificationId, int mediaType, String url) {
+    private void updateNotification(
+            int notificationId, int mediaType, String url, boolean isIncognito) {
         if (doesNotificationExist(notificationId)
                 && !doesNotificationNeedUpdate(notificationId, mediaType))  {
             return;
         }
         destroyNotification(notificationId);
         if (mediaType != MEDIATYPE_NO_MEDIA) {
-            createNotification(notificationId, mediaType, url);
+            createNotification(notificationId, mediaType, url, isIncognito);
         }
         if (mNotifications.size() == 0) stopSelf();
     }
@@ -162,7 +167,8 @@
      * @param mediaType Media type of the notification.
      * @param url Url of the current webrtc call.
      */
-    private void createNotification(int notificationId, int mediaType, String url) {
+    private void createNotification(
+            int notificationId, int mediaType, String url, boolean isIncognito) {
         final String channelId = mediaType == MEDIATYPE_SCREEN_CAPTURE
                 ? ChannelDefinitions.ChannelId.SCREEN_CAPTURE
                 : ChannelDefinitions.ChannelId.MEDIA;
@@ -172,13 +178,9 @@
                         .createChromeNotificationBuilder(true /* preferCompat */, channelId)
                         .setAutoCancel(false)
                         .setOngoing(true)
-                        .setContentTitle(
-                                ContextUtils.getApplicationContext().getString(R.string.app_name))
                         .setSmallIcon(getNotificationIconId(mediaType))
                         .setLocalOnly(true);
 
-        StringBuilder contentText =
-                new StringBuilder(getNotificationContentText(mediaType, url)).append('.');
         Intent tabIntent = Tab.createBringTabToFrontIntent(notificationId);
         if (tabIntent != null) {
             PendingIntent contentIntent = PendingIntent.getActivity(
@@ -193,17 +195,40 @@
                         ContextUtils.getApplicationContext().getResources().getString(
                                 R.string.accessibility_stop),
                         buildStopCapturePendingIntent(notificationId));
-            } else {
-                contentText.append(" ").append(
+            }
+        }
+
+        boolean hideUserData = isIncognito
+                && ChromeFeatureList.isEnabled(
+                           ChromeFeatureList.HIDE_USER_DATA_FROM_INCOGNITO_NOTIFICATIONS);
+
+        StringBuilder descriptionText =
+                new StringBuilder(getNotificationContentText(mediaType, url, hideUserData))
+                        .append('.');
+
+        String contentText;
+        if (hideUserData) {
+            builder.setSubText(ContextUtils.getApplicationContext().getResources().getString(
+                    R.string.notification_incognito_tab));
+            builder.setContentTitle(descriptionText.toString());
+            contentText = ContextUtils.getApplicationContext().getResources().getString(
+                    R.string.media_notification_link_text_incognito);
+        } else {
+            if (tabIntent == null) {
+                descriptionText.append(" ").append(url);
+            } else if (mediaType != MEDIATYPE_SCREEN_CAPTURE) {
+                descriptionText.append(" ").append(
                         ContextUtils.getApplicationContext().getResources().getString(
                                 R.string.media_notification_link_text, url));
             }
-        } else {
-            contentText.append(" ").append(url);
-        }
-        builder.setContentText(contentText.toString());
 
-        Notification notification = builder.buildWithBigTextStyle(contentText.toString());
+            builder.setContentTitle(
+                    ContextUtils.getApplicationContext().getString(R.string.app_name));
+            contentText = descriptionText.toString();
+        }
+        builder.setContentText(contentText);
+
+        Notification notification = builder.buildWithBigTextStyle(contentText);
         mNotificationManager.notify(NOTIFICATION_NAMESPACE, notificationId, notification);
         mNotifications.put(notificationId, mediaType);
         updateSharedPreferencesEntry(notificationId, false);
@@ -217,19 +242,27 @@
      * @param url Url of the current webrtc call.
      * @return A string builder initialized to the contents of the specified string.
      */
-    private String getNotificationContentText(int mediaType, String url) {
+    private String getNotificationContentText(int mediaType, String url, boolean hideUserData) {
         if (mediaType == MEDIATYPE_SCREEN_CAPTURE) {
-            return ContextUtils.getApplicationContext().getResources().getString(
-                    R.string.screen_capture_notification_text, url);
+            return ContextUtils.getApplicationContext().getResources().getString(hideUserData
+                            ? R.string.screen_capture_incognito_notification_text
+                            : R.string.screen_capture_notification_text,
+                    url);
         }
 
         int notificationContentTextId = 0;
         if (mediaType == MEDIATYPE_AUDIO_AND_VIDEO) {
-            notificationContentTextId = R.string.video_audio_call_notification_text_2;
+            notificationContentTextId = hideUserData
+                    ? R.string.video_audio_call_incognito_notification_text_2
+                    : R.string.video_audio_call_notification_text_2;
         } else if (mediaType == MEDIATYPE_VIDEO_ONLY) {
-            notificationContentTextId = R.string.video_call_notification_text_2;
+            notificationContentTextId = hideUserData
+                    ? R.string.video_call_incognito_notification_text_2
+                    : R.string.video_call_notification_text_2;
         } else if (mediaType == MEDIATYPE_AUDIO_ONLY) {
-            notificationContentTextId = R.string.audio_call_notification_text_2;
+            notificationContentTextId = hideUserData
+                    ? R.string.audio_call_incognito_notification_text_2
+                    : R.string.audio_call_notification_text_2;
         }
 
         return ContextUtils.getApplicationContext().getResources().getString(
@@ -347,6 +380,10 @@
         }
         intent.putExtra(NOTIFICATION_MEDIA_URL_EXTRA, baseUrl);
         intent.putExtra(NOTIFICATION_MEDIA_TYPE_EXTRA, mediaType);
+        if (TabWindowManager.getInstance().getTabById(tabId) != null) {
+            intent.putExtra(NOTIFICATION_MEDIA_IS_INCOGNITO,
+                    TabWindowManager.getInstance().getTabById(tabId).isIncognito());
+        }
         context.startService(intent);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageViewMD.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageViewMD.java
index abd3de3..dcc72bb6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageViewMD.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageViewMD.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.util.ViewUtils.dpToPx;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.support.annotation.IdRes;
@@ -17,7 +18,6 @@
 import android.text.style.BulletSpan;
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.widget.ImageView;
@@ -35,7 +35,6 @@
  */
 public class IncognitoNewTabPageViewMD extends IncognitoNewTabPageView {
     private final Context mContext;
-    private final DisplayMetrics mMetrics;
 
     private int mWidthDp;
     private int mHeightDp;
@@ -68,21 +67,15 @@
     public IncognitoNewTabPageViewMD(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-        mMetrics = mContext.getResources().getDisplayMetrics();
-    }
-
-    private int pxToDp(int px) {
-        return (int) Math.ceil(px / mMetrics.density);
-    }
-
-    private int spToPx(int sp) {
-        return (int) Math.ceil(sp * mMetrics.scaledDensity);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mWidthDp = mContext.getResources().getConfiguration().screenWidthDp;
+        mHeightDp = mContext.getResources().getConfiguration().screenHeightDp;
+
         populateBulletpoints(R.id.new_tab_incognito_features, R.string.new_tab_otr_not_saved);
         populateBulletpoints(R.id.new_tab_incognito_warning, R.string.new_tab_otr_visible);
 
@@ -95,21 +88,28 @@
                         (TextView) findViewById(R.id.new_tab_incognito_warning), mLearnMore};
         mBulletpointsContainer =
                 (LinearLayout) findViewById(R.id.new_tab_incognito_bulletpoints_container);
+
+        adjustView();
+    }
+
+    private void adjustView() {
+        adjustIcon();
+        adjustLayout();
+        adjustLearnMore();
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        if (changed) {
-            mWidthDp = pxToDp(getMeasuredWidth());
-            mHeightDp = pxToDp(getMeasuredHeight());
-
-            adjustTypography();
-            adjustIcon();
-            adjustLayout();
-            adjustLearnMore();
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // View#onConfigurationChanged() doesn't get called when resizing this view in
+        // multi-window mode, so #onMeasure() is used instead.
+        Configuration config = getContext().getResources().getConfiguration();
+        if (mWidthDp != config.screenWidthDp || mHeightDp != config.screenHeightDp) {
+            mWidthDp = config.screenWidthDp;
+            mHeightDp = config.screenHeightDp;
+            adjustView();
         }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
     /**
@@ -152,25 +152,6 @@
                 new SpanApplier.SpanInfo("<li3>", "</li3>", new IncognitoBulletSpan())));
     }
 
-    /** Adjusts the font settings. */
-    private void adjustTypography() {
-        if (mWidthDp <= 240 || mHeightDp <= 320) {
-            // Small text on small screens.
-            mHeader.setTextSize(20 /* sp */);
-            mHeader.setLineSpacing(spToPx(4) /* add */, 1 /* mult */); // 20sp + 4sp = 24sp
-
-            for (TextView paragraph : mParagraphs) paragraph.setTextSize(12 /* sp */);
-        } else {
-            // Large text on large screens.
-            mHeader.setTextSize(24 /* sp */);
-            mHeader.setLineSpacing(spToPx(8) /* add */, 1 /* mult */); // 24sp + 8sp = 32sp
-
-            for (TextView paragraph : mParagraphs) paragraph.setTextSize(14 /* sp */);
-        }
-
-        // Paragraph line spacing is constant +6sp, defined in R.layout.new_tab_page_incognito_md.
-    }
-
     /** Adjusts the paddings, margins, and the orientation of bulletpoints. */
     private void adjustLayout() {
         int paddingHorizontalDp;
@@ -187,8 +168,6 @@
             mContainer.setGravity(Gravity.START);
 
             // Decide the bulletpoints orientation.
-            // TODO (thildebr): This is never set to anything but false, check if we can remove
-            // related code.
             bulletpointsArrangedHorizontally = false;
 
             // The subtitle is sized automatically, but not wider than CONTENT_WIDTH_DP.
@@ -212,16 +191,9 @@
             mContainer.setGravity(Gravity.CENTER_HORIZONTAL);
 
             // Decide the bulletpoints orientation.
-            int totalBulletpointsWidthDp = pxToDp(mBulletpointsContainer.getChildAt(0).getWidth())
-                    + pxToDp(mBulletpointsContainer.getChildAt(1).getWidth())
-                    + BULLETPOINTS_HORIZONTAL_SPACING_DP;
-            bulletpointsArrangedHorizontally = totalBulletpointsWidthDp <= CONTENT_WIDTH_DP;
+            bulletpointsArrangedHorizontally = true;
 
-            // The subtitle width is equal to the two sets of bulletpoints if they are arranged
-            // horizontally. If not, use the default CONTENT_WIDTH_DP.
-            int contentWidthPx = bulletpointsArrangedHorizontally
-                    ? dpToPx(mContext, totalBulletpointsWidthDp)
-                    : dpToPx(mContext, CONTENT_WIDTH_DP);
+            int contentWidthPx = dpToPx(mContext, CONTENT_WIDTH_DP);
             mSubtitle.setLayoutParams(new LinearLayout.LayoutParams(
                     contentWidthPx, LinearLayout.LayoutParams.WRAP_CONTENT));
             mBulletpointsContainer.setLayoutParams(new LinearLayout.LayoutParams(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 7e5042b7..bf8c4536 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -15,6 +15,7 @@
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.DiscardableReferencePool;
@@ -54,10 +55,12 @@
 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.util.ColorUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.common.BrowserControlsState;
 import org.chromium.net.NetworkChangeNotifier;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
@@ -312,6 +315,11 @@
             public void onPageLoadStarted(Tab tab, String url) {
                 saveLastScrollPosition();
             }
+
+            @Override
+            public void onBrowserControlsConstraintsUpdated(Tab tab, int constraints) {
+                updateMargins(constraints);
+            }
         };
         mTab.addObserver(mTabObserver);
         updateSearchProviderHasLogo();
@@ -368,6 +376,28 @@
                 index, NAVIGATION_ENTRY_SCROLL_POSITION_KEY, Integer.toString(scrollPosition));
     }
 
+    /** Update the margins for the content when browser controls constraints are changed. */
+    protected void updateMargins(@BrowserControlsState int constraints) {
+        // TODO(mdjones): can this be merged with BasicNativePage's updateMargins?
+
+        View view = getView();
+        ViewGroup.MarginLayoutParams layoutParams =
+                ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
+        if (layoutParams == null) {
+            // We could be updating the margin before the root view is attached to window.
+            layoutParams = new ViewGroup.MarginLayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+            view.setLayoutParams(layoutParams);
+        }
+
+        int bottomMargin = 0;
+        if (FeatureUtilities.isBottomToolbarEnabled()
+                && constraints != BrowserControlsState.HIDDEN) {
+            bottomMargin = mTab.getActivity().getFullscreenManager().getBottomControlsHeight();
+        }
+        layoutParams.bottomMargin = bottomMargin;
+    }
+
     /** @return The view container for the new tab page. */
     @VisibleForTesting
     public NewTabPageView getNewTabPageView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 884fa69..230416a3a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.TileGroup;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 
@@ -277,11 +276,6 @@
         // immediately attached to the window if the RecyclerView is scrolled when the NTP
         // is refocused.
         if (mManager.isLocationBarShownInNTP()) mNewTabPageLayout.updateSearchBoxOnScroll();
-
-        if (FeatureUtilities.isBottomToolbarEnabled()) {
-            ((MarginLayoutParams) getLayoutParams()).bottomMargin =
-                    getResources().getDimensionPixelSize(R.dimen.bottom_toolbar_height);
-        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
index df0b902..d86fa2c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
@@ -55,9 +55,10 @@
 
         // PhotoPickerListener:
         @Override
-        public void onPhotoPickerUserAction(Action action, String[] photos) {
+        public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
             mExternalIntentSelected = false;
-            if (action == Action.LAUNCH_GALLERY || action == Action.LAUNCH_CAMERA) {
+            if (action == PhotoPickerAction.LAUNCH_GALLERY
+                    || action == PhotoPickerAction.LAUNCH_CAMERA) {
                 mExternalIntentSelected = true;
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
index 2bc0c5d..6df7d7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -216,7 +216,7 @@
         mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
             @Override
             public void onCancel(DialogInterface dialog) {
-                executeAction(PhotoPickerListener.Action.CANCEL, null, ACTION_CANCEL);
+                executeAction(PhotoPickerListener.PhotoPickerAction.CANCEL, null, ACTION_CANCEL);
             }
         });
     }
@@ -264,7 +264,7 @@
         if (view.getId() == R.id.done) {
             notifyPhotosSelected();
         } else {
-            executeAction(PhotoPickerListener.Action.CANCEL, null, ACTION_CANCEL);
+            executeAction(PhotoPickerListener.PhotoPickerAction.CANCEL, null, ACTION_CANCEL);
         }
     }
 
@@ -320,14 +320,14 @@
      * Notifies the listener that the user selected to launch the gallery.
      */
     public void showGallery() {
-        executeAction(PhotoPickerListener.Action.LAUNCH_GALLERY, null, ACTION_BROWSE);
+        executeAction(PhotoPickerListener.PhotoPickerAction.LAUNCH_GALLERY, null, ACTION_BROWSE);
     }
 
     /**
      * Notifies the listener that the user selected to launch the camera intent.
      */
     public void showCamera() {
-        executeAction(PhotoPickerListener.Action.LAUNCH_CAMERA, null, ACTION_NEW_PHOTO);
+        executeAction(PhotoPickerListener.PhotoPickerAction.LAUNCH_CAMERA, null, ACTION_NEW_PHOTO);
     }
 
     /**
@@ -381,7 +381,8 @@
             photos[i++] = bitmap.getFilePath();
         }
 
-        executeAction(PhotoPickerListener.Action.PHOTOS_SELECTED, photos, ACTION_PHOTO_PICKED);
+        executeAction(
+                PhotoPickerListener.PhotoPickerAction.PHOTOS_SELECTED, photos, ACTION_PHOTO_PICKED);
     }
 
     /**
@@ -427,7 +428,8 @@
      * @param photos The photos that were selected (if any).
      * @param umaId The UMA value to record with the action.
      */
-    private void executeAction(PhotoPickerListener.Action action, String[] photos, int umaId) {
+    private void executeAction(
+            @PhotoPickerListener.PhotoPickerAction int action, String[] photos, int umaId) {
         mListener.onPhotoPickerUserAction(action, photos);
         mDialog.dismiss();
         UiUtils.onPhotoPickerDismissed();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 37a5b95..c0ead26 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2857,15 +2857,15 @@
       <message name="IDS_NTP_EXPLORE_SITES_MORE" desc="Text on a button leading to a complete view of all categories for all popular websites">
         More categories
       </message>
+      <message name="IDS_EXPLORE_SITES_DEFAULT_CATEGORY_NEWS" desc="The caption for a button that when clicked opens a UI in this tab that shows a list of websites with content related to news. [CHAR-LIMIT=12]">
+        News
+      </message>
       <message name="IDS_EXPLORE_SITES_DEFAULT_CATEGORY_SPORTS" desc="The caption for a button that when clicked opens a UI in this tab that shows a list of websites with content related to sports. For example, soccer, cricket, football, running [CHAR-LIMIT=12]">
         Sports
       </message>
       <message name="IDS_EXPLORE_SITES_DEFAULT_CATEGORY_SHOPPING" desc="The caption for a button that when clicked opens a UI in this tab that shows a list of websites with content related to shopping. [CHAR-LIMIT=12]">
         Shopping
       </message>
-      <message name="IDS_EXPLORE_SITES_DEFAULT_CATEGORY_COOKING" desc="The caption for a button that when clicked opens a UI in this tab that shows a list of websites with content related to food and cooking. [CHAR-LIMIT=12]">
-        Cooking
-      </message>
       <message name="IDS_EXPLORE_SITES_LOADING_FROM_NET" desc="Caption for an indeterminate loading spinner indicating that we are loading the data from the internet">
         Finding the best from the web.
       </message>
@@ -3314,6 +3314,9 @@
       <message name="IDS_MEDIA_NOTIFICATION_LINK_TEXT" desc="Url of the current tab. The notification will display this text for the user to identify the tab to return to.">
         Touch to return to <ph name="URL_OF_THE_CURRENT_TAB">%1$s<ex>https://apprtc.appspot.com</ex></ph>
       </message>
+      <message name="IDS_MEDIA_NOTIFICATION_LINK_TEXT_INCOGNITO" desc="The notification will display this text for the user to return to the incognito tab which has created the notification.">
+        Tap to go to site
+      </message>
       <message name="IDS_MEDIA_NOTIFICATION_INCOGNITO" desc="Text used as a placeholder for media notifications content, when notification is shown from incognito tab.">
         A site is playing media
       </message>
@@ -3323,6 +3326,9 @@
       <message name="IDS_SCREEN_CAPTURE_NOTIFICATION_TEXT" desc="Text to be shown as a notification when screen capture is in progress.">
         <ph name="URL_OF_THE_CURRENT_TAB">%1$s<ex>https://apprtc.appspot.com</ex></ph> is sharing your screen
       </message>
+      <message name="IDS_SCREEN_CAPTURE_INCOGNITO_NOTIFICATION_TEXT" desc="Text to be shown as a notification when screen capture is in progress in incognito mode.">
+        A site is sharing your screen
+      </message>
       <message name="IDS_NEW_TAB_INCOGNITO_HEADER" desc="Header shown when a user opens an incognito tab explaining incognito mode">
         You’ve gone incognito.
       </message>
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_COOKING.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_COOKING.png.sha1
deleted file mode 100644
index 3650503..0000000
--- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_COOKING.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3dbcd93f6154cce922e8285a06d6d7eccbf42e71
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_NEWS.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_NEWS.png.sha1
new file mode 100644
index 0000000..daa7908
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_EXPLORE_SITES_DEFAULT_CATEGORY_NEWS.png.sha1
@@ -0,0 +1 @@
+549c3db2e92edce8c13b3da616116daf893fc7e7
\ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c5bd8060..36bcc41 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -109,6 +109,7 @@
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryView.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
index 99a7fdd..9593417 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
@@ -20,10 +20,12 @@
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.ImeAdapter;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.Criteria;
@@ -141,6 +143,9 @@
         AutofillLogger.setLoggerForTesting(
                 logEntry -> mAutofillLoggedEntries.add(logEntry)
         );
+        // TODO(crbug.com/894428) - fix this suite to use the embedded test server instead of
+        // data urls.
+        Features.getInstance().enable(ChromeFeatureList.AUTOFILL_ALLOW_NON_HTTP_ACTIVATION);
     }
 
     private void loadAndFillForm(final String formDataUrl, final String inputText)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
index 32258d8..cad2681 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
@@ -8,6 +8,7 @@
 import android.view.ViewGroup;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -18,10 +19,12 @@
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -43,6 +46,13 @@
     public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
             new ChromeActivityTestRule<>(ChromeActivity.class);
 
+    @Before
+    public void setUp() throws Exception {
+        // TODO(crbug.com/894428) - fix this suite to use the embedded test server instead of
+        // data urls.
+        Features.getInstance().enable(ChromeFeatureList.AUTOFILL_ALLOW_NON_HTTP_ACTIVATION);
+    }
+
     /**
      * Test that showing autofill popup and keyboard will not hide the autofill popup.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
index a06f34a..07a87afc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -25,6 +25,7 @@
 import android.view.ViewGroup;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,6 +66,13 @@
 
     private final ManualFillingTestHelper mHelper = new ManualFillingTestHelper(mActivityTestRule);
 
+    @Before
+    public void setUp() throws Exception {
+        // TODO(crbug.com/894428) - fix this suite to use the embedded test server instead of
+        // data urls.
+        Features.getInstance().enable(ChromeFeatureList.AUTOFILL_ALLOW_NON_HTTP_ACTIVATION);
+    }
+
     @After
     public void tearDown() {
         mHelper.clear();
@@ -214,9 +222,11 @@
                 "", "80666", "", "Disneyland", "1", "a.turing@enigma.com", "DE"));
         mHelper.createTestTab();
 
-        // Focus the field to bring up the accessory.
-        mHelper.clickEmailField();
+        // Focus the field to bring up the autofill popup. We force a accessory here because the
+        // autofill popup doesn't trigger on password fields.
+        mHelper.clickEmailField(true);
         mHelper.waitForKeyboard();
+
         DropdownPopupWindowInterface popup = mHelper.waitForAutofillPopup("a.tu");
 
         assertThat(popup.isShowing(), is(true));
@@ -231,6 +241,24 @@
 
     @Test
     @SmallTest
+    @Features.DisableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY})
+    public void testSelectingNonPasswordInputDismissesAccessory()
+            throws InterruptedException, TimeoutException, ExecutionException {
+        mHelper.loadTestPage(false);
+        mHelper.createTestTab();
+
+        // Focus the password field to bring up the accessory.
+        mHelper.clickPasswordField();
+        mHelper.waitForKeyboard();
+        whenDisplayed(withId(R.id.tabs));
+
+        // Clicking the email field hides the accessory again.
+        mHelper.clickEmailField(false);
+        mHelper.waitToBeHidden(withId(R.id.keyboard_accessory));
+    }
+
+    @Test
+    @SmallTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     public void testInvokingTabSwitcherHidesAccessory()
             throws InterruptedException, TimeoutException {
@@ -284,7 +312,7 @@
                 () -> { mActivityTestRule.getActivity().onResumeWithNative(); });
 
         // Clicking the field should bring the accessory back up.
-        mHelper.clickEmailField();
+        mHelper.clickPasswordField();
         mHelper.waitForKeyboard();
 
         // Click the tab to show the sheet and hide the keyboard.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
index 903cc48..b2b1ec7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
@@ -24,7 +24,6 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewInteraction;
-import android.support.v7.content.res.AppCompatResources;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -168,11 +167,18 @@
 
     public void clickPasswordField() throws TimeoutException, InterruptedException {
         DOMUtils.clickNode(mWebContentsRef.get(), "password");
+        requestShowKeyboardAccessory();
         mKeyboard.showKeyboard(null);
     }
 
-    public void clickEmailField() throws TimeoutException, InterruptedException {
+    public void clickEmailField(boolean forceAccessory)
+            throws TimeoutException, InterruptedException {
         DOMUtils.clickNode(mWebContentsRef.get(), "email");
+        if (forceAccessory) {
+            requestShowKeyboardAccessory();
+        } else {
+            requestHideKeyboardAccessory();
+        }
         mKeyboard.showKeyboard(null);
     }
 
@@ -220,15 +226,17 @@
     }
 
     /**
-     * Creates and adds an empty tab without listener to keyboard accessory and sheet.
+     * Creates and adds a password tab to keyboard accessory and sheet.
      */
     public void createTestTab() {
-        mActivityTestRule.getActivity().getManualFillingController().getMediatorForTesting().addTab(
-                new KeyboardAccessoryData.Tab(
-                        AppCompatResources.getDrawable(InstrumentationRegistry.getContext(),
-                                android.R.drawable.ic_lock_lock),
-                        "TestTabDescription", R.layout.empty_accessory_sheet, AccessoryTabType.ALL,
-                        null));
+        KeyboardAccessoryData.Provider<KeyboardAccessoryData.Item> provider =
+                new KeyboardAccessoryData.PropertyProvider<>();
+        mActivityTestRule.getActivity().getManualFillingController().registerPasswordProvider(
+                provider);
+        provider.notifyObservers(new KeyboardAccessoryData.Item[] {
+                KeyboardAccessoryData.Item.createSuggestion("TestName", "", false, null, null),
+                KeyboardAccessoryData.Item.createSuggestion(
+                        "TestPassword", "", false, (item) -> {}, null)});
     }
 
     /**
@@ -277,4 +285,30 @@
             waitForView((ViewGroup) r, matcher, VIEW_INVISIBLE | VIEW_NULL | VIEW_GONE);
         });
     }
+
+    /**
+     * In order to make sure the keyboard accessory is only shown on appropriate fields, a request
+     * to show is usually sent from the native side. This method simulates that request.
+     */
+    private void requestShowKeyboardAccessory() {
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mActivityTestRule.getActivity()
+                    .getManualFillingController()
+                    .getMediatorForTesting()
+                    .showWhenKeyboardIsVisible();
+        });
+    }
+
+    /**
+     * In order to make sure the keyboard accessory is only shown on appropriate fields, a request
+     * from the native side can request to hide it. This method simulates that request.
+     */
+    private void requestHideKeyboardAccessory() {
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mActivityTestRule.getActivity()
+                    .getManualFillingController()
+                    .getMediatorForTesting()
+                    .hide();
+        });
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
index 1fb4391..2f1cefd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java
@@ -32,6 +32,7 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,6 +47,7 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 
@@ -66,6 +68,13 @@
 
     private final ManualFillingTestHelper mHelper = new ManualFillingTestHelper(mActivityTestRule);
 
+    @Before
+    public void setUp() throws Exception {
+        // TODO(crbug.com/894428) - fix this suite to use the embedded test server instead of
+        // data urls.
+        Features.getInstance().enable(ChromeFeatureList.AUTOFILL_ALLOW_NON_HTTP_ACTIVATION);
+    }
+
     @After
     public void tearDown() {
         mHelper.clear();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
index c3090a6..ef7bf33 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java
@@ -66,7 +66,7 @@
     private SelectionDelegate<ContactDetails> mSelectionDelegate;
 
     // The last action recorded in the dialog (e.g. photo selected).
-    private ContactsPickerAction mLastActionRecorded;
+    private @ContactsPickerAction int mLastActionRecorded;
 
     // The final set of contacts picked by the dialog (json string).
     private String mLastSelectedContacts;
@@ -101,7 +101,7 @@
     // ContactsPickerDialog.ContactsPickerListener:
 
     @Override
-    public void onContactsPickerUserAction(ContactsPickerAction action, String contacts) {
+    public void onContactsPickerUserAction(@ContactsPickerAction int action, String contacts) {
         mLastActionRecorded = action;
         mLastSelectedContacts = contacts;
         onActionCallback.notifyCalled();
@@ -158,7 +158,7 @@
     }
 
     private void clickDone() throws Exception {
-        mLastActionRecorded = null;
+        mLastActionRecorded = ContactsPickerAction.NUM_ENTRIES;
 
         ContactsPickerToolbar toolbar =
                 (ContactsPickerToolbar) mDialog.findViewById(R.id.action_bar);
@@ -170,7 +170,7 @@
     }
 
     public void clickCancel() throws Exception {
-        mLastActionRecorded = null;
+        mLastActionRecorded = ContactsPickerAction.NUM_ENTRIES;
 
         PickerCategoryView categoryView = mDialog.getCategoryViewForTesting();
         View cancel = new View(mActivityTestRule.getActivity());
@@ -181,8 +181,8 @@
     }
 
     private void clickActionButton(final int expectedSelectionCount,
-            final ContactsPickerAction expectedAction) throws Exception {
-        mLastActionRecorded = null;
+            final @ContactsPickerAction int expectedAction) throws Exception {
+        mLastActionRecorded = ContactsPickerAction.NUM_ENTRIES;
 
         ContactsPickerToolbar toolbar =
                 (ContactsPickerToolbar) mDialog.findViewById(R.id.action_bar);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 3a73720..76bca0e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -7,7 +7,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Bundle;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSession;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -17,6 +21,7 @@
 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.PathUtils;
@@ -33,11 +38,13 @@
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
+import org.chromium.net.NetError;
 import org.chromium.net.test.EmbeddedTestServer;
 
 import java.util.ArrayList;
@@ -50,6 +57,8 @@
 public class DetachedResourceRequestTest {
     @Rule
     public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
+    @Rule
+    public TestRule mProcessor = new Features.InstrumentationProcessor();
 
     private CustomTabsConnection mConnection;
     private Context mContext;
@@ -57,6 +66,7 @@
 
     private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
     private static final Uri ORIGIN = Uri.parse("http://cats.google.com");
+    private static final int NET_OK = 0;
 
     @Before
     public void setUp() throws Exception {
@@ -78,7 +88,6 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testCanDoParallelRequest() throws Exception {
         CustomTabsSessionToken session = CustomTabsSessionToken.createMockSessionTokenForTesting();
         Assert.assertTrue(mConnection.newSession(session));
@@ -126,7 +135,6 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testStartParallelRequestValidation() throws Exception {
         CustomTabsSessionToken session = prepareSession();
 
@@ -203,9 +211,8 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
+    @EnableFeatures(ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)
     public void testCanStartParallelRequest() throws Exception {
-        CustomTabsSessionToken session = prepareSession();
         final CallbackHelper cb = new CallbackHelper();
         setUpTestServerWithListener(new EmbeddedTestServer.ConnectionListener() {
             @Override
@@ -213,18 +220,71 @@
                 cb.notifyCalled();
             }
         });
-
         Uri url = Uri.parse(mServer.getURL("/echotitle"));
+
+        DetachedResourceRequestCheckCallback customTabsCallback =
+                new DetachedResourceRequestCheckCallback(
+                        url, CustomTabsConnection.ParallelRequestStatus.SUCCESS, NET_OK);
+        CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback);
+
         ThreadUtils.runOnUiThread(() -> {
             Assert.assertEquals(CustomTabsConnection.ParallelRequestStatus.SUCCESS,
                     mConnection.handleParallelRequest(session, prepareIntent(url, ORIGIN)));
         });
+        customTabsCallback.waitForRequest();
         cb.waitForCallback(0, 1);
+        customTabsCallback.waitForCompletion();
     }
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_RESOURCE_PREFETCH)
+    @EnableFeatures(ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)
+    public void testParallelRequestFailureCallback() throws Exception {
+        Uri url = Uri.parse("http://request-url");
+        int status =
+                CustomTabsConnection.ParallelRequestStatus.FAILURE_INVALID_REFERRER_FOR_SESSION;
+        DetachedResourceRequestCheckCallback customTabsCallback =
+                new DetachedResourceRequestCheckCallback(url, status, 0);
+        CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback);
+
+        ThreadUtils.runOnUiThread(() -> {
+            Assert.assertEquals(status,
+                    mConnection.handleParallelRequest(
+                            session, prepareIntent(url, Uri.parse("http://not-the-right-origin"))));
+        });
+        customTabsCallback.waitForRequest();
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)
+    public void testParallelRequestCompletionFailureCallback() throws Exception {
+        final CallbackHelper cb = new CallbackHelper();
+        setUpTestServerWithListener(new EmbeddedTestServer.ConnectionListener() {
+            @Override
+            public void readFromSocket(long socketId) {
+                cb.notifyCalled();
+            }
+        });
+        Uri url = Uri.parse(mServer.getURL("/close-socket"));
+
+        DetachedResourceRequestCheckCallback customTabsCallback =
+                new DetachedResourceRequestCheckCallback(url,
+                        CustomTabsConnection.ParallelRequestStatus.SUCCESS,
+                        Math.abs(NetError.ERR_EMPTY_RESPONSE));
+        CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback);
+
+        ThreadUtils.runOnUiThread(() -> {
+            Assert.assertEquals(CustomTabsConnection.ParallelRequestStatus.SUCCESS,
+                    mConnection.handleParallelRequest(session, prepareIntent(url, ORIGIN)));
+        });
+        customTabsCallback.waitForRequest();
+        cb.waitForCallback(0, 1);
+        customTabsCallback.waitForCompletion();
+    }
+
+    @Test
+    @SmallTest
     public void testCanStartResourcePrefetch() throws Exception {
         CustomTabsSessionToken session = prepareSession();
         final CallbackHelper cb = new CallbackHelper();
@@ -249,15 +309,22 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
+    @EnableFeatures(ChromeFeatureList.CCT_REPORT_PARALLEL_REQUEST_STATUS)
     public void testCanSetCookie() throws Exception {
-        CustomTabsSessionToken session = prepareSession();
         mServer = EmbeddedTestServer.createAndStartServer(mContext);
         final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie"));
+
+        DetachedResourceRequestCheckCallback customTabsCallback =
+                new DetachedResourceRequestCheckCallback(
+                        url, CustomTabsConnection.ParallelRequestStatus.SUCCESS, NET_OK);
+        CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback);
+
         ThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertEquals(CustomTabsConnection.ParallelRequestStatus.SUCCESS,
                     mConnection.handleParallelRequest(session, prepareIntent(url, ORIGIN)));
         });
+        customTabsCallback.waitForRequest();
+        customTabsCallback.waitForCompletion();
 
         String echoUrl = mServer.getURL("/echoheader?Cookie");
         Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(mContext, echoUrl);
@@ -275,7 +342,6 @@
      */
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testSafeBrowsingMainResource() throws Exception {
         SafeBrowsingApiBridge.setSafeBrowsingHandlerType(
                 new MockSafeBrowsingApiHandler().getClass());
@@ -309,7 +375,6 @@
      */
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testSafeBrowsingSubresource() throws Exception {
         SafeBrowsingApiBridge.setSafeBrowsingHandlerType(
                 new MockSafeBrowsingApiHandler().getClass());
@@ -337,7 +402,6 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testCanBlockThirdPartyCookies() throws Exception {
         CustomTabsSessionToken session = prepareSession();
         mServer = EmbeddedTestServer.createAndStartServer(mContext);
@@ -364,7 +428,6 @@
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
     public void testThirdPartyCookieBlockingAllowsFirstParty() throws Exception {
         CustomTabsTestUtils.warmUpAndWait();
         mServer = EmbeddedTestServer.createAndStartServer(mContext);
@@ -393,22 +456,28 @@
     }
 
     private CustomTabsSessionToken prepareSession() throws Exception {
-        return prepareSession(ORIGIN);
+        return prepareSession(ORIGIN, null);
     }
 
     private CustomTabsSessionToken prepareSession(Uri origin) throws Exception {
-        final CustomTabsSessionToken session =
-                CustomTabsSessionToken.createMockSessionTokenForTesting();
-        Assert.assertTrue(mConnection.newSession(session));
-        mConnection.mClientManager.setAllowParallelRequestForSession(session, true);
-        mConnection.mClientManager.setAllowResourcePrefetchForSession(session, true);
+        return prepareSession(origin, null);
+    }
+
+    private CustomTabsSessionToken prepareSession(Uri origin, CustomTabsCallback callback)
+            throws Exception {
+        CustomTabsSession session = CustomTabsTestUtils.bindWithCallback(callback);
+        Intent intent = (new CustomTabsIntent.Builder(session)).build().intent;
+        CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(mConnection.newSession(token));
+        mConnection.mClientManager.setAllowParallelRequestForSession(token, true);
+        mConnection.mClientManager.setAllowResourcePrefetchForSession(token, true);
         CustomTabsTestUtils.warmUpAndWait();
         ThreadUtils.runOnUiThreadBlocking(() -> {
             OriginVerifier.addVerifiedOriginForPackage(mContext.getPackageName(),
                     new Origin(origin.toString()), CustomTabsService.RELATION_USE_AS_ORIGIN);
-            Assert.assertTrue(mConnection.canDoParallelRequest(session, origin));
+            Assert.assertTrue(mConnection.canDoParallelRequest(token, origin));
         });
-        return session;
+        return token;
     }
 
     private void setUpTestServerWithListener(EmbeddedTestServer.ConnectionListener listener)
@@ -456,4 +525,45 @@
         intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, referrer);
         return intent;
     }
+
+    private static class DetachedResourceRequestCheckCallback extends CustomTabsCallback {
+        private final Uri mExpectedUrl;
+        private final int mExpectedRequestStatus;
+        private final int mExpectedFinalStatus;
+        private final CallbackHelper mRequestedWaiter = new CallbackHelper();
+        private final CallbackHelper mCompletionWaiter = new CallbackHelper();
+
+        public DetachedResourceRequestCheckCallback(
+                Uri expectedUrl, int expectedRequestStatus, int expectedFinalStatus) {
+            super();
+            mExpectedUrl = expectedUrl;
+            mExpectedRequestStatus = expectedRequestStatus;
+            mExpectedFinalStatus = expectedFinalStatus;
+        }
+
+        @Override
+        public void extraCallback(String callbackName, Bundle args) {
+            if (CustomTabsConnection.ON_DETACHED_REQUEST_REQUESTED.equals(callbackName)) {
+                Uri url = args.getParcelable("url");
+                int status = args.getInt("status");
+                Assert.assertEquals(mExpectedUrl, url);
+                Assert.assertEquals(mExpectedRequestStatus, status);
+                mRequestedWaiter.notifyCalled();
+            } else if (CustomTabsConnection.ON_DETACHED_REQUEST_COMPLETED.equals(callbackName)) {
+                Uri url = args.getParcelable("url");
+                int status = args.getInt("net_error");
+                Assert.assertEquals(mExpectedUrl, url);
+                Assert.assertEquals(mExpectedFinalStatus, status);
+                mCompletionWaiter.notifyCalled();
+            }
+        }
+
+        public void waitForRequest() throws InterruptedException, TimeoutException {
+            mRequestedWaiter.waitForCallback(0, 1);
+        }
+
+        public void waitForCompletion() throws InterruptedException, TimeoutException {
+            mCompletionWaiter.waitForCallback(0, 1);
+        }
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
index b39060d..addf0d169 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadMediaParserTest.java
@@ -15,7 +15,6 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
 import org.chromium.content_public.browser.test.util.Criteria;
@@ -82,7 +81,6 @@
     @Test
     @LargeTest
     @Feature({"Download"})
-    @RetryOnFailure
     /**
      * Verify that the metadata from audio file can be retrieved correctly.
      * @throws InterruptedException
@@ -96,7 +94,6 @@
     @Test
     @LargeTest
     @Feature({"Download"})
-    @RetryOnFailure
     /**
      * Verify metadata and thumbnail can be retrieved correctly from h264 video file.
      * @throws InterruptedException
@@ -114,7 +111,6 @@
     @Test
     @LargeTest
     @Feature({"Download"})
-    @RetryOnFailure
     /**
      * Verify metadata and thumbnail can be retrieved correctly from vp8 video file.
      * @throws InterruptedException
@@ -132,7 +128,6 @@
     @Test
     @LargeTest
     @Feature({"Download"})
-    @RetryOnFailure
     /**
      * Verify metadata and thumbnail can be retrieved correctly from vp8 video file with alpha
      * plane.
@@ -147,4 +142,17 @@
         Assert.assertTrue(
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
+
+    @Test
+    @LargeTest
+    @Feature({"Download"})
+    /**
+     * Verify graceful failure on parsing invalid video file.
+     * @throws InterruptedException
+     */
+    public void testParseInvalidVideoFile() throws Exception {
+        File invalidFile = File.createTempFile("test", "webm");
+        MediaParserResult result = parseMediaFile(invalidFile.getAbsolutePath(), "video/webm");
+        Assert.assertTrue("Should fail to parse invalid video.", result.mediaData == null);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
index ab05ac3..76f8377 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
@@ -64,7 +64,7 @@
     private SelectionDelegate<PickerBitmap> mSelectionDelegate;
 
     // The last action recorded in the dialog (e.g. photo selected).
-    private Action mLastActionRecorded;
+    private @PhotoPickerAction int mLastActionRecorded;
 
     // The final set of photos picked by the dialog. Can be an empty array, if
     // nothing was selected.
@@ -100,7 +100,7 @@
     // PhotoPickerDialog.PhotoPickerListener:
 
     @Override
-    public void onPhotoPickerUserAction(Action action, String[] photos) {
+    public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
         mLastActionRecorded = action;
         mLastSelectedPhotos = photos != null ? photos.clone() : null;
         if (mLastSelectedPhotos != null) Arrays.sort(mLastSelectedPhotos);
@@ -169,25 +169,25 @@
     }
 
     private void clickDone() throws Exception {
-        mLastActionRecorded = null;
+        mLastActionRecorded = PhotoPickerAction.NUM_ENTRIES;
 
         PhotoPickerToolbar toolbar = (PhotoPickerToolbar) mDialog.findViewById(R.id.action_bar);
         Button done = (Button) toolbar.findViewById(R.id.done);
         int callCount = onActionCallback.getCallCount();
         TouchCommon.singleClickView(done);
         onActionCallback.waitForCallback(callCount, 1);
-        Assert.assertEquals(PhotoPickerListener.Action.PHOTOS_SELECTED, mLastActionRecorded);
+        Assert.assertEquals(PhotoPickerAction.PHOTOS_SELECTED, mLastActionRecorded);
     }
 
     public void clickCancel() throws Exception {
-        mLastActionRecorded = null;
+        mLastActionRecorded = PhotoPickerAction.NUM_ENTRIES;
 
         PickerCategoryView categoryView = mDialog.getCategoryViewForTesting();
         View cancel = new View(mActivityTestRule.getActivity());
         int callCount = onActionCallback.getCallCount();
         categoryView.onClick(cancel);
         onActionCallback.waitForCallback(callCount, 1);
-        Assert.assertEquals(PhotoPickerListener.Action.CANCEL, mLastActionRecorded);
+        Assert.assertEquals(PhotoPickerAction.CANCEL, mLastActionRecorded);
     }
 
     private void dismissDialog() {
@@ -212,7 +212,7 @@
         clickCancel();
 
         Assert.assertNull(mLastSelectedPhotos);
-        Assert.assertEquals(PhotoPickerListener.Action.CANCEL, mLastActionRecorded);
+        Assert.assertEquals(PhotoPickerAction.CANCEL, mLastActionRecorded);
 
         dismissDialog();
     }
@@ -232,7 +232,7 @@
         clickDone();
 
         Assert.assertEquals(1, mLastSelectedPhotos.length);
-        Assert.assertEquals(PhotoPickerListener.Action.PHOTOS_SELECTED, mLastActionRecorded);
+        Assert.assertEquals(PhotoPickerAction.PHOTOS_SELECTED, mLastActionRecorded);
         Assert.assertEquals(mTestFiles.get(1).getFilePath(), mLastSelectedPhotos[0]);
 
         dismissDialog();
@@ -254,7 +254,7 @@
         clickDone();
 
         Assert.assertEquals(3, mLastSelectedPhotos.length);
-        Assert.assertEquals(PhotoPickerListener.Action.PHOTOS_SELECTED, mLastActionRecorded);
+        Assert.assertEquals(PhotoPickerAction.PHOTOS_SELECTED, mLastActionRecorded);
         Assert.assertEquals(mTestFiles.get(0).getFilePath(), mLastSelectedPhotos[0]);
         Assert.assertEquals(mTestFiles.get(2).getFilePath(), mLastSelectedPhotos[1]);
         Assert.assertEquals(mTestFiles.get(4).getFilePath(), mLastSelectedPhotos[2]);
diff --git a/chrome/android/webapk/libs/runtime_library/BUILD.gn b/chrome/android/webapk/libs/runtime_library/BUILD.gn
index 0911aa8e..477d5f7 100644
--- a/chrome/android/webapk/libs/runtime_library/BUILD.gn
+++ b/chrome/android/webapk/libs/runtime_library/BUILD.gn
@@ -49,19 +49,16 @@
   ]
 }
 
-dist_jar("webapk_runtime_library") {
-  requires_android = true
+proguarded_dist_dex("webapk_runtime_library") {
   deps = [
     ":runtime_library_for_assets_java",
   ]
-  proguard_enabled = true
   proguard_configs = [
     "runtime_library.proguard.flags",
     "//base/android/proguard/chromium_code.flags",
     "//base/android/proguard/chromium_apk.flags",
   ]
-  output = "$target_gen_dir/$target_name.jar"
-  dex_path = "$target_gen_dir/$runtime_library_dex_asset_name"
+  output = "$target_gen_dir/$runtime_library_dex_asset_name"
 }
 
 android_assets("runtime_library_assets") {
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 069bab9..bb7bbbd 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -548,7 +548,7 @@
             &amp;Go to <ph name="URL">$1<ex>http://www.google.com/</ex></ph>
           </message>
           <message name="IDS_CONTENT_CONTEXT_GENERATEPASSWORD" desc="The name of the Generate Password command in the content area context menu">
-            Generate password...
+            Suggest password...
           </message>
           <message name="IDS_CONTENT_CONTEXT_MORE_APPS" desc="The label for the parent item of the submenu in the content area context menu.">
             More
@@ -769,7 +769,7 @@
             &amp;Go to <ph name="URL">$1<ex>http://www.google.com/</ex></ph>
           </message>
           <message name="IDS_CONTENT_CONTEXT_GENERATEPASSWORD" desc="In Title Case: The name of the Generate Password command in the content area context menu">
-            Generate Password...
+            Suggest Password...
           </message>
         </if>
       </if>
@@ -9031,6 +9031,15 @@
       <message name="IDS_VIDEO_AUDIO_CALL_NOTIFICATION_TEXT_2" desc="Text to be shown as a notification when a WebRTC video and audio call is in progress" formatter_data="android_java">
         Accessing audio and video input
       </message>
+      <message name="IDS_VIDEO_CALL_INCOGNITO_NOTIFICATION_TEXT_2" desc="Text to be shown as a notification when a WebRTC video call is in progress in incognito mode." formatter_data="android_java">
+        A site is accessing video input
+      </message>
+      <message name="IDS_AUDIO_CALL_INCOGNITO_NOTIFICATION_TEXT_2" desc="Text to be shown as a notification when a WebRTC audio call is in progress in incognito mode." formatter_data="android_java">
+        A site is accessing audio input
+      </message>
+      <message name="IDS_VIDEO_AUDIO_CALL_INCOGNITO_NOTIFICATION_TEXT_2" desc="Text to be shown as a notification when a WebRTC video and audio call is in progress in incognito mode." formatter_data="android_java">
+        A site is accessing audio and video input
+      </message>
     </if>
 
     <!-- Download open confirmation dialog -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1496cf80..7e10bfa6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3090,6 +3090,7 @@
       "badging/badge_service_impl.h",
       "component_updater/cros_component_installer_chromeos.cc",
       "component_updater/cros_component_installer_chromeos.h",
+      "component_updater/cros_component_manager.h",
       "component_updater/metadata_table_chromeos.cc",
       "component_updater/metadata_table_chromeos.h",
       "download/notification/download_item_notification.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9c7281399..11a160d2 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -65,6 +65,7 @@
 #include "components/flags_ui/feature_entry_macros.h"
 #include "components/flags_ui/flags_storage.h"
 #include "components/flags_ui/flags_ui_switches.h"
+#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/language/core/common/language_experiments.h"
 #include "components/nacl/common/buildflags.h"
 #include "components/nacl/common/nacl_switches.h"
@@ -431,6 +432,25 @@
      switches::kChromeHomeSwipeLogicType, "velocity"},
 };
 
+const FeatureEntry::FeatureParam kCCTModuleCache_ZeroMinutes[] = {
+    {"cct_module_cache_time_limit_ms", "0"}};
+const FeatureEntry::FeatureParam kCCTModuleCache_OneMinute[] = {
+    {"cct_module_cache_time_limit_ms", "60000"}};
+const FeatureEntry::FeatureParam kCCTModuleCache_FiveMinutes[] = {
+    {"cct_module_cache_time_limit_ms", "300000"}};
+const FeatureEntry::FeatureParam kCCTModuleCache_ThirtyMinutes[] = {
+    {"cct_module_cache_time_limit_ms", "1800000"}};
+const FeatureEntry::FeatureVariation kCCTModuleCacheVariations[] = {
+    {"0 minutes", kCCTModuleCache_ZeroMinutes,
+     base::size(kCCTModuleCache_ZeroMinutes), nullptr},
+    {"1 minute", kCCTModuleCache_OneMinute,
+     base::size(kCCTModuleCache_OneMinute), nullptr},
+    {"5 minutes", kCCTModuleCache_FiveMinutes,
+     base::size(kCCTModuleCache_FiveMinutes), nullptr},
+    {"30 minutes", kCCTModuleCache_ThirtyMinutes,
+     base::size(kCCTModuleCache_ThirtyMinutes), nullptr},
+};
+
 #endif  // OS_ANDROID
 
 const FeatureEntry::Choice kNumRasterThreadsChoices[] = {
@@ -2377,9 +2397,6 @@
      SINGLE_VALUE_TYPE(switches::kTrySupportedChannelLayouts)},
 #endif  // OS_WIN
 #if defined(OS_MACOSX)
-    {"mac-v2-sandbox", flag_descriptions::kMacV2SandboxName,
-     flag_descriptions::kMacV2SandboxDescription, kOsMac,
-     FEATURE_VALUE_TYPE(features::kMacV2Sandbox)},
     {"mac-views-task-manager", flag_descriptions::kMacViewsTaskManagerName,
      flag_descriptions::kMacViewsTaskManagerDescription, kOsMac,
      FEATURE_VALUE_TYPE(features::kViewsTaskManager)},
@@ -4165,7 +4182,9 @@
      FEATURE_VALUE_TYPE(chrome::android::kCCTModule)},
     {"cct-module-cache", flag_descriptions::kCCTModuleCacheName,
      flag_descriptions::kCCTModuleCacheDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kCCTModuleCache)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kCCTModuleCache,
+                                    kCCTModuleCacheVariations,
+                                    "CCTModule")},
 #endif
 
     {"enable-css-fragment-identifiers",
@@ -4421,6 +4440,10 @@
      flag_descriptions::kAndroidSiteSettingsUIDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidSiteSettingsUI)},
 #endif
+    {"fcm-invalidations", flag_descriptions::kFCMInvalidationsName,
+     flag_descriptions::kFCMInvalidationsDescription, kOsAll,
+     FEATURE_VALUE_TYPE(invalidation::switches::kFCMInvalidations)},
+
 };
 
 class FlagsStateSingleton {
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 256dbaf..10bc0d1 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -85,6 +85,7 @@
     &kCCTParallelRequest,
     &kCCTPostMessageAPI,
     &kCCTRedirectPreconnect,
+    &kCCTReportParallelRequestStatus,
     &kCCTResourcePrefetch,
     &kChromeDuetFeature,
     &kChromeHomeSwipeLogic,
@@ -230,6 +231,9 @@
 const base::Feature kCCTRedirectPreconnect{"CCTRedirectPreconnect",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kCCTReportParallelRequestStatus{
+    "CCTReportParallelRequestStatus", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCCTResourcePrefetch{"CCTResourcePrefetch",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index b0b0ebf..00b50b8 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -27,6 +27,7 @@
 extern const base::Feature kCCTParallelRequest;
 extern const base::Feature kCCTPostMessageAPI;
 extern const base::Feature kCCTRedirectPreconnect;
+extern const base::Feature kCCTReportParallelRequestStatus;
 extern const base::Feature kCCTResourcePrefetch;
 extern const base::Feature kChromeDuetFeature;
 extern const base::Feature kChromeHomeSwipeLogic;
diff --git a/chrome/browser/android/customtabs/detached_resource_request.cc b/chrome/browser/android/customtabs/detached_resource_request.cc
index f8c6fd6..56839fb 100644
--- a/chrome/browser/android/customtabs/detached_resource_request.cc
+++ b/chrome/browser/android/customtabs/detached_resource_request.cc
@@ -139,7 +139,9 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   int net_error = url_loader_->NetError();
   bool success = net_error == net::OK;
+  net_error = std::abs(net_error);
   auto duration = base::TimeTicks::Now() - start_time_;
+
   switch (motivation_) {
     case Motivation::kParallelRequest: {
       if (success) {
@@ -158,7 +160,7 @@
       }
 
       base::UmaHistogramSparse("CustomTabs.DetachedResourceRequest.FinalStatus",
-                               std::abs(net_error));
+                               net_error);
       break;
     }
     case Motivation::kResourcePrefetch: {
@@ -171,12 +173,12 @@
       }
 
       base::UmaHistogramSparse("CustomTabs.ResourcePrefetch.FinalStatus",
-                               std::abs(net_error));
+                               net_error);
       break;
     }
   }
 
-  std::move(cb_).Run(success);
+  std::move(cb_).Run(net_error);
 }
 
 }  // namespace customtabs
diff --git a/chrome/browser/android/customtabs/detached_resource_request.h b/chrome/browser/android/customtabs/detached_resource_request.h
index 8c997331a..0ad15e8 100644
--- a/chrome/browser/android/customtabs/detached_resource_request.h
+++ b/chrome/browser/android/customtabs/detached_resource_request.h
@@ -44,13 +44,13 @@
   // GENERATED_JAVA_CLASS_NAME_OVERRIDE: DetachedResourceRequestMotivation
   enum class Motivation { kParallelRequest, kResourcePrefetch };
 
-  using OnResultCallback = base::OnceCallback<void(bool success)>;
+  using OnResultCallback = base::OnceCallback<void(int net_error)>;
 
   ~DetachedResourceRequest();
 
   // Creates a detached request to a |url|, with a given initiating URL,
   // |first_party_for_cookies|. Called on the UI thread.
-  // Optional |cb| to get notified about the fetch result, for testing.
+  // Optional |cb| to get notified about the fetch result.
   static void CreateAndStart(content::BrowserContext* browser_context,
                              const GURL& url,
                              const GURL& first_party_for_cookies,
diff --git a/chrome/browser/android/customtabs/detached_resource_request_android.cc b/chrome/browser/android/customtabs/detached_resource_request_android.cc
index 0d6e282..be65186 100644
--- a/chrome/browser/android/customtabs/detached_resource_request_android.cc
+++ b/chrome/browser/android/customtabs/detached_resource_request_android.cc
@@ -5,6 +5,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
 #include "chrome/browser/android/customtabs/detached_resource_request.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
@@ -15,10 +16,25 @@
 
 namespace customtabs {
 
+namespace {
+
+void NotifyClientOfDetachedRequestCompletion(
+    const base::android::ScopedJavaGlobalRef<jobject>& session,
+    const GURL& url,
+    int net_error) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_CustomTabsConnection_notifyClientOfDetachedRequestCompletion(
+      env, session, base::android::ConvertUTF8ToJavaString(env, url.spec()),
+      net_error);
+}
+
+}  // namespace
+
 static void JNI_CustomTabsConnection_CreateAndStartDetachedResourceRequest(
     JNIEnv* env,
     const base::android::JavaParamRef<jclass>& jcaller,
     const base::android::JavaParamRef<jobject>& profile,
+    const base::android::JavaParamRef<jobject>& session,
     const base::android::JavaParamRef<jstring>& url,
     const base::android::JavaParamRef<jstring>& origin,
     jint referrer_policy,
@@ -39,9 +55,17 @@
           static_cast<blink::WebReferrerPolicy>(referrer_policy));
   DetachedResourceRequest::Motivation request_motivation =
       static_cast<DetachedResourceRequest::Motivation>(motivation);
+
+  DetachedResourceRequest::OnResultCallback cb =
+      session.is_null()
+          ? base::DoNothing()
+          : base::BindOnce(&NotifyClientOfDetachedRequestCompletion,
+                           base::android::ScopedJavaGlobalRef<jobject>(session),
+                           native_url);
+
   DetachedResourceRequest::CreateAndStart(
       native_profile, native_url, native_origin, url_request_referrer_policy,
-      request_motivation);
+      request_motivation, std::move(cb));
 }
 
 }  // namespace customtabs
diff --git a/chrome/browser/android/customtabs/detached_resource_request_unittest.cc b/chrome/browser/android/customtabs/detached_resource_request_unittest.cc
index b3224578..8cd628d0 100644
--- a/chrome/browser/android/customtabs/detached_resource_request_unittest.cc
+++ b/chrome/browser/android/customtabs/detached_resource_request_unittest.cc
@@ -228,8 +228,8 @@
 
     DetachedResourceRequest::CreateAndStart(
         browser_context(), url, site_for_cookies, policy, kMotivation,
-        base::BindLambdaForTesting([&](bool success) {
-          EXPECT_TRUE(success);
+        base::BindLambdaForTesting([&](int net_error) {
+          EXPECT_EQ(net::OK, net_error);
           request_completion_waiter.Quit();
         }));
     server_request_waiter.Run();
@@ -260,8 +260,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         request_completion_waiter.Quit();
       }));
   server_request_waiter.Run();
@@ -285,8 +285,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_FALSE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_NE(net::OK, net_error);
         request_waiter.Quit();
       }));
   request_waiter.Run();
@@ -389,8 +389,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         request_completion_waiter.Quit();
       }));
 
@@ -450,8 +450,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         detached_request_waiter.Quit();
       }));
   first_request_waiter.Run();
@@ -481,8 +481,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         request_waiter.Quit();
       }));
   request_waiter.Run();
@@ -505,8 +505,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_FALSE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(-net::ERR_TOO_MANY_REDIRECTS, net_error);
         request_waiter.Quit();
       }));
   request_waiter.Run();
@@ -534,8 +534,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         first_request_waiter.Quit();
       }));
   first_request_waiter.Run();
@@ -543,8 +543,8 @@
   DetachedResourceRequest::CreateAndStart(
       browser_context(), url, site_for_cookies,
       content::Referrer::GetDefaultReferrerPolicy(), kMotivation,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](int net_error) {
+        EXPECT_EQ(net::OK, net_error);
         second_request_waiter.Quit();
       }));
   second_request_waiter.Run();
diff --git a/chrome/browser/android/download/download_media_parser.cc b/chrome/browser/android/download/download_media_parser.cc
index 78e6cd6..4491002f 100644
--- a/chrome/browser/android/download/download_media_parser.cc
+++ b/chrome/browser/android/download/download_media_parser.cc
@@ -156,14 +156,15 @@
 void DownloadMediaParser::OnVideoFrameRetrieved(
     bool success,
     chrome::mojom::VideoFrameDataPtr video_frame_data,
-    const media::VideoDecoderConfig& config) {
+    const base::Optional<media::VideoDecoderConfig>& config) {
   if (!success) {
     OnError();
     return;
   }
 
   video_frame_data_ = std::move(video_frame_data);
-  config_ = config;
+  DCHECK(config.has_value());
+  config_ = config.value();
 
   // For vp8, vp9 codec, we directly do software decoding in utility process.
   // Render now.
diff --git a/chrome/browser/android/download/download_media_parser.h b/chrome/browser/android/download/download_media_parser.h
index 48c6b82..a4e1e22 100644
--- a/chrome/browser/android/download/download_media_parser.h
+++ b/chrome/browser/android/download/download_media_parser.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/common/media_galleries/metadata_types.h"
 #include "chrome/services/media_gallery_util/public/cpp/media_parser_provider.h"
@@ -69,9 +70,10 @@
 
   // Retrieves an encoded video frame.
   void RetrieveEncodedVideoFrame();
-  void OnVideoFrameRetrieved(bool success,
-                             chrome::mojom::VideoFrameDataPtr video_frame_data,
-                             const media::VideoDecoderConfig& config);
+  void OnVideoFrameRetrieved(
+      bool success,
+      chrome::mojom::VideoFrameDataPtr video_frame_data,
+      const base::Optional<media::VideoDecoderConfig>& config);
 
   // Decodes the video frame.
   void OnGpuVideoAcceleratorFactoriesReady(
diff --git a/chrome/browser/android/password_manager/password_accessory_view_android.cc b/chrome/browser/android/password_manager/password_accessory_view_android.cc
index 44d412f..efbd901 100644
--- a/chrome/browser/android/password_manager/password_accessory_view_android.cc
+++ b/chrome/browser/android/password_manager/password_accessory_view_android.cc
@@ -74,6 +74,16 @@
       base::android::AttachCurrentThread(), java_object_);
 }
 
+void PasswordAccessoryViewAndroid::ShowWhenKeyboardIsVisible() {
+  Java_PasswordAccessoryBridge_showWhenKeyboardIsVisible(
+      base::android::AttachCurrentThread(), java_object_);
+}
+
+void PasswordAccessoryViewAndroid::Hide() {
+  Java_PasswordAccessoryBridge_hide(base::android::AttachCurrentThread(),
+                                    java_object_);
+}
+
 void PasswordAccessoryViewAndroid::OnAutomaticGenerationStatusChanged(
     bool available) {
   if (!available && java_object_.is_null())
diff --git a/chrome/browser/android/password_manager/password_accessory_view_android.h b/chrome/browser/android/password_manager/password_accessory_view_android.h
index 1499613..1b4276e8 100644
--- a/chrome/browser/android/password_manager/password_accessory_view_android.h
+++ b/chrome/browser/android/password_manager/password_accessory_view_android.h
@@ -31,6 +31,8 @@
   void OnAutomaticGenerationStatusChanged(bool available) override;
   void CloseAccessorySheet() override;
   void SwapSheetWithKeyboard() override;
+  void ShowWhenKeyboardIsVisible() override;
+  void Hide() override;
 
   // Called from Java via JNI:
   void OnFaviconRequested(
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc
index a46c5eb..dd13c9a 100644
--- a/chrome/browser/android/preferences/pref_service_bridge.cc
+++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -433,7 +433,7 @@
     const JavaParamRef<jobject>& obj) {
   PrefService* pref_service = GetPrefService();
   return pref_service->IsManagedPreference(
-      safe_browsing::GetExtendedReportingPrefName(*pref_service));
+      prefs::kSafeBrowsingScoutReportingEnabled);
 }
 
 static jboolean JNI_PrefServiceBridge_GetSafeBrowsingEnabled(
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 4d24541..7f35ff1a 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -87,7 +88,6 @@
 
 namespace {
 
-static const char kDataURIPrefix[] = "data:text/html;charset=utf-8,";
 static const char kTestShippingFormString[] =
     "<form action=\"http://www.example.com/\" method=\"POST\">"
     "<label for=\"firstname\">First name:</label>"
@@ -246,6 +246,8 @@
 
     https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
     https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
+    https_server_.RegisterRequestHandler(base::BindRepeating(
+        &AutofillInteractiveTestBase::HandleTestURL, base::Unretained(this)));
     ASSERT_TRUE(https_server_.InitializeAndListen());
     https_server_.StartAcceptingConnections();
 
@@ -256,6 +258,8 @@
 
     // Ensure that |embedded_test_server()| serves both domains used below.
     host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
+        &AutofillInteractiveTestBase::HandleTestURL, base::Unretained(this)));
     embedded_test_server()->StartAcceptingConnections();
 
     // By default, all SSL cert checks are valid. Can be overriden in tests if
@@ -278,6 +282,18 @@
     AutofillUiTest::TearDownInProcessBrowserTestFixture();
   }
 
+  std::unique_ptr<net::test_server::HttpResponse> HandleTestURL(
+      const net::test_server::HttpRequest& request) {
+    if (request.relative_url != kTestUrlPath)
+      return nullptr;
+
+    auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+    response->set_code(net::HTTP_OK);
+    response->set_content_type("text/html;charset=utf-8");
+    response->set_content(test_url_content_);
+    return std::move(response);
+  }
+
   content::WebContents* GetWebContents() {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
@@ -599,8 +615,16 @@
                           {ObservedUiEvents::kFormDataFilled}, widget);
   }
 
+  GURL GetTestUrl() const { return https_server_.GetURL(kTestUrlPath); }
+
+  void SetTestUrlResponse(std::string content) {
+    test_url_content_ = std::move(content);
+  }
+
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
+  static const char kTestUrlPath[];
+
  private:
   net::EmbeddedTestServer https_server_;
 
@@ -624,9 +648,15 @@
   std::unique_ptr<net::test_server::ControllableHttpResponse>
       controllable_http_response_;
 
+  // The response to return for queries to |kTestUrlPath|
+  std::string test_url_content_;
+
   DISALLOW_COPY_AND_ASSIGN(AutofillInteractiveTestBase);
 };
 
+const char AutofillInteractiveTestBase::kTestUrlPath[] =
+    "/internal/test_url_path";
+
 // AutofillInteractiveTest ----------------------------------------------------
 
 class AutofillInteractiveTest : public AutofillInteractiveTestBase {
@@ -648,8 +678,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   TryBasicFormFill();
@@ -658,9 +689,11 @@
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, BasicClear) {
   CreateTestProfile();
 
+  SetTestUrlResponse(kTestShippingFormString);
+
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   TryBasicFormFill();
 
@@ -671,9 +704,10 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-                      kTestBillingFormString)));
+  SetTestUrlResponse(
+      base::StrCat({kTestShippingFormString, kTestBillingFormString}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Fill first section.
   TryBasicFormFill();
@@ -707,8 +741,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Modify a field.
   FocusFieldByName("city");
@@ -735,8 +770,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Modify a field.
   FocusFieldByName("state");
@@ -770,10 +806,10 @@
       "document.getElementById('phone').value = '15142223344';"
       "</script>";
 
-  // Load the test page and prefill it with the above script.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-                      kPrefillScript)));
+  // Load the test page.
+  SetTestUrlResponse(base::StrCat({kTestShippingFormString, kPrefillScript}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   CreateTestProfile();
 
@@ -791,8 +827,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   TryBasicFormFill();
 
@@ -851,8 +888,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   TryBasicFormFill();
 
@@ -891,8 +929,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
   TryBasicFormFill();
 
   // Change the last name.
@@ -933,8 +972,9 @@
   CreateSecondTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   TryBasicFormFill();
 
@@ -966,8 +1006,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Focus a fillable field.
   FocusFirstNameField();
@@ -982,8 +1023,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Focus a fillable field.
   FocusFirstNameField();
@@ -998,8 +1040,10 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
+
   // Focus a fillable field.
   ASSERT_NO_FATAL_FAILURE(FocusFirstNameField());
 
@@ -1074,8 +1118,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // If AutofillSingleClick is NOT enabled, then the first time we click on the
   // first name field, nothing should happen.
@@ -1107,13 +1152,15 @@
 // Makes sure that clicking outside the focused field doesn't activate
 // the popup.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, DontAutofillForOutsideClick) {
+  static const char kDisabledButton[] =
+      "<button disabled id='disabled-button'>Cant click this</button>";
+
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(),
-      GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-           "<button disabled id='disabled-button'>Cant click this</button>")));
+  SetTestUrlResponse(base::StrCat({kTestShippingFormString, kDisabledButton}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   ASSERT_NO_FATAL_FAILURE(FocusFirstNameField());
 
@@ -1134,8 +1181,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke and accept the Autofill popup and verify the form was filled.
   FocusFirstNameField();
@@ -1174,18 +1222,21 @@
 #endif
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest,
                        MAYBE_OnSelectOptionFromDatalist) {
+  static const char kTestForm[] =
+      "<form action=\"http://www.example.com/\" method=\"POST\">"
+      "  <input list=\"dl\" type=\"search\" id=\"firstname\"><br>"
+      "  <datalist id=\"dl\">"
+      "  <option value=\"Adam\"></option>"
+      "  <option value=\"Bob\"></option>"
+      "  <option value=\"Carl\"></option>"
+      "  </datalist>"
+      "</form>";
+
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(),
-      GURL(std::string(kDataURIPrefix) +
-           "<form action=\"http://www.example.com/\" method=\"POST\">"
-           "  <input list=\"dl\" type=\"search\" id=\"firstname\"><br>"
-           "  <datalist id=\"dl\">"
-           "  <option value=\"Adam\"></option>"
-           "  <option value=\"Bob\"></option>"
-           "  <option value=\"Carl\"></option>"
-           "  </datalist>"
-           "</form>")));
+  SetTestUrlResponse(kTestForm);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
+
   std::string orginalcolor;
   GetFieldBackgroundColor("firstname", &orginalcolor);
 
@@ -1202,9 +1253,7 @@
 
 // Test that a JavaScript oninput event is fired after auto-filling a form.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, OnInputAfterAutofill) {
-  CreateTestProfile();
-
-  const char kOnInputScript[] =
+  static const char kOnInputScript[] =
       "<script>"
       "focused_fired = false;"
       "unfocused_fired = false;"
@@ -1225,10 +1274,12 @@
       "document.getElementById('country').value = 'US';"
       "</script>";
 
+  CreateTestProfile();
+
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-                      kOnInputScript)));
+  SetTestUrlResponse(base::StrCat({kTestShippingFormString, kOnInputScript}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   FocusFirstNameField();
@@ -1273,9 +1324,7 @@
 
 // Test that a JavaScript onchange event is fired after auto-filling a form.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, OnChangeAfterAutofill) {
-  CreateTestProfile();
-
-  const char kOnChangeScript[] =
+  static const char kOnChangeScript[] =
       "<script>"
       "focused_fired = false;"
       "unfocused_fired = false;"
@@ -1296,10 +1345,12 @@
       "document.getElementById('country').value = 'US';"
       "</script>";
 
+  CreateTestProfile();
+
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-                      kOnChangeScript)));
+  SetTestUrlResponse(base::StrCat({kTestShippingFormString, kOnChangeScript}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   FocusFirstNameField();
@@ -1343,9 +1394,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, InputFiresBeforeChange) {
-  CreateTestProfile();
-
-  const char kInputFiresBeforeChangeScript[] =
+  static const char kInputFiresBeforeChangeScript[] =
       "<script>"
       "inputElementEvents = [];"
       "function recordInputElementEvent(e) {"
@@ -1363,10 +1412,13 @@
       "document.getElementById('country').onchange = recordSelectElementEvent;"
       "</script>";
 
+  CreateTestProfile();
+
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString +
-                      kInputFiresBeforeChangeScript)));
+  SetTestUrlResponse(
+      base::StrCat({kTestShippingFormString, kInputFiresBeforeChangeScript}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke and accept the Autofill popup and verify the form was filled.
   FocusFirstNameField();
@@ -1421,11 +1473,7 @@
 // Test that we can autofill forms distinguished only by their |id| attribute.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest,
                        AutofillFormsDistinguishedById) {
-  CreateTestProfile();
-
-  // Load the test page.
-  const std::string kURL =
-      std::string(kDataURIPrefix) + kTestShippingFormString +
+  static const char kScript[] =
       "<script>"
       "var mainForm = document.forms[0];"
       "mainForm.id = 'mainForm';"
@@ -1435,7 +1483,13 @@
       "newForm.id = 'newForm';"
       "mainForm.parentNode.insertBefore(newForm, mainForm);"
       "</script>";
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(), GURL(kURL)));
+
+  CreateTestProfile();
+
+  // Load the test page.
+  SetTestUrlResponse(base::StrCat({kTestShippingFormString, kScript}));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   TryBasicFormFill();
@@ -1446,43 +1500,46 @@
 // (duplicated for "confirmation"); or variants that are hot-swapped via
 // JavaScript, with only one actually visible at any given time.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, AutofillFormWithRepeatedField) {
+  static const char kForm[] =
+      "<form action=\"http://www.example.com/\" method=\"POST\">"
+      "<label for=\"firstname\">First name:</label>"
+      " <input type=\"text\" id=\"firstname\""
+      "        onfocus=\"domAutomationController.send(true)\"><br>"
+      "<label for=\"lastname\">Last name:</label>"
+      " <input type=\"text\" id=\"lastname\"><br>"
+      "<label for=\"address1\">Address line 1:</label>"
+      " <input type=\"text\" id=\"address1\"><br>"
+      "<label for=\"address2\">Address line 2:</label>"
+      " <input type=\"text\" id=\"address2\"><br>"
+      "<label for=\"city\">City:</label>"
+      " <input type=\"text\" id=\"city\"><br>"
+      "<label for=\"state\">State:</label>"
+      " <select id=\"state\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">California</option>"
+      " <option value=\"TX\">Texas</option>"
+      " </select><br>"
+      "<label for=\"state_freeform\" style=\"display:none\">State:</label>"
+      " <input type=\"text\" id=\"state_freeform\""
+      "        style=\"display:none\"><br>"
+      "<label for=\"zip\">ZIP code:</label>"
+      " <input type=\"text\" id=\"zip\"><br>"
+      "<label for=\"country\">Country:</label>"
+      " <select id=\"country\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">Canada</option>"
+      " <option value=\"US\">United States</option>"
+      " </select><br>"
+      "<label for=\"phone\">Phone number:</label>"
+      " <input type=\"text\" id=\"phone\"><br>"
+      "</form>";
+
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
-      GURL(std::string(kDataURIPrefix) +
-           "<form action=\"http://www.example.com/\" method=\"POST\">"
-           "<label for=\"firstname\">First name:</label>"
-           " <input type=\"text\" id=\"firstname\""
-           "        onfocus=\"domAutomationController.send(true)\"><br>"
-           "<label for=\"lastname\">Last name:</label>"
-           " <input type=\"text\" id=\"lastname\"><br>"
-           "<label for=\"address1\">Address line 1:</label>"
-           " <input type=\"text\" id=\"address1\"><br>"
-           "<label for=\"address2\">Address line 2:</label>"
-           " <input type=\"text\" id=\"address2\"><br>"
-           "<label for=\"city\">City:</label>"
-           " <input type=\"text\" id=\"city\"><br>"
-           "<label for=\"state\">State:</label>"
-           " <select id=\"state\">"
-           " <option value=\"\" selected=\"yes\">--</option>"
-           " <option value=\"CA\">California</option>"
-           " <option value=\"TX\">Texas</option>"
-           " </select><br>"
-           "<label for=\"state_freeform\" style=\"display:none\">State:</label>"
-           " <input type=\"text\" id=\"state_freeform\""
-           "        style=\"display:none\"><br>"
-           "<label for=\"zip\">ZIP code:</label>"
-           " <input type=\"text\" id=\"zip\"><br>"
-           "<label for=\"country\">Country:</label>"
-           " <select id=\"country\">"
-           " <option value=\"\" selected=\"yes\">--</option>"
-           " <option value=\"CA\">Canada</option>"
-           " <option value=\"US\">United States</option>"
-           " </select><br>"
-           "<label for=\"phone\">Phone number:</label>"
-           " <input type=\"text\" id=\"phone\"><br>"
-           "</form>")));
+  SetTestUrlResponse(kForm);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   TryBasicFormFill();
@@ -1492,42 +1549,45 @@
 // Test that we properly autofill forms with non-autofillable fields.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest,
                        AutofillFormWithNonAutofillableField) {
+  static const char kForm[] =
+      "<form action=\"http://www.example.com/\" method=\"POST\">"
+      "<label for=\"firstname\">First name:</label>"
+      " <input type=\"text\" id=\"firstname\""
+      "        onfocus=\"domAutomationController.send(true)\"><br>"
+      "<label for=\"middlename\">Middle name:</label>"
+      " <input type=\"text\" id=\"middlename\" autocomplete=\"off\" /><br>"
+      "<label for=\"lastname\">Last name:</label>"
+      " <input type=\"text\" id=\"lastname\"><br>"
+      "<label for=\"address1\">Address line 1:</label>"
+      " <input type=\"text\" id=\"address1\"><br>"
+      "<label for=\"address2\">Address line 2:</label>"
+      " <input type=\"text\" id=\"address2\"><br>"
+      "<label for=\"city\">City:</label>"
+      " <input type=\"text\" id=\"city\"><br>"
+      "<label for=\"state\">State:</label>"
+      " <select id=\"state\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">California</option>"
+      " <option value=\"TX\">Texas</option>"
+      " </select><br>"
+      "<label for=\"zip\">ZIP code:</label>"
+      " <input type=\"text\" id=\"zip\"><br>"
+      "<label for=\"country\">Country:</label>"
+      " <select id=\"country\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">Canada</option>"
+      " <option value=\"US\">United States</option>"
+      " </select><br>"
+      "<label for=\"phone\">Phone number:</label>"
+      " <input type=\"text\" id=\"phone\"><br>"
+      "</form>";
+
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
-      GURL(std::string(kDataURIPrefix) +
-           "<form action=\"http://www.example.com/\" method=\"POST\">"
-           "<label for=\"firstname\">First name:</label>"
-           " <input type=\"text\" id=\"firstname\""
-           "        onfocus=\"domAutomationController.send(true)\"><br>"
-           "<label for=\"middlename\">Middle name:</label>"
-           " <input type=\"text\" id=\"middlename\" autocomplete=\"off\" /><br>"
-           "<label for=\"lastname\">Last name:</label>"
-           " <input type=\"text\" id=\"lastname\"><br>"
-           "<label for=\"address1\">Address line 1:</label>"
-           " <input type=\"text\" id=\"address1\"><br>"
-           "<label for=\"address2\">Address line 2:</label>"
-           " <input type=\"text\" id=\"address2\"><br>"
-           "<label for=\"city\">City:</label>"
-           " <input type=\"text\" id=\"city\"><br>"
-           "<label for=\"state\">State:</label>"
-           " <select id=\"state\">"
-           " <option value=\"\" selected=\"yes\">--</option>"
-           " <option value=\"CA\">California</option>"
-           " <option value=\"TX\">Texas</option>"
-           " </select><br>"
-           "<label for=\"zip\">ZIP code:</label>"
-           " <input type=\"text\" id=\"zip\"><br>"
-           "<label for=\"country\">Country:</label>"
-           " <select id=\"country\">"
-           " <option value=\"\" selected=\"yes\">--</option>"
-           " <option value=\"CA\">Canada</option>"
-           " <option value=\"US\">United States</option>"
-           " </select><br>"
-           "<label for=\"phone\">Phone number:</label>"
-           " <input type=\"text\" id=\"phone\"><br>"
-           "</form>")));
+  SetTestUrlResponse(kForm);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   TryBasicFormFill();
@@ -1535,84 +1595,87 @@
 
 // Test that we can Autofill dynamically generated forms.
 IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, DynamicFormFill) {
+  static const char kDynamicForm[] =
+      "<form id=\"form\" action=\"http://www.example.com/\""
+      "      method=\"POST\"></form>"
+      "<script>"
+      "function AddElement(name, label) {"
+      "  var form = document.getElementById('form');"
+      ""
+      "  var label_text = document.createTextNode(label);"
+      "  var label_element = document.createElement('label');"
+      "  label_element.setAttribute('for', name);"
+      "  label_element.appendChild(label_text);"
+      "  form.appendChild(label_element);"
+      ""
+      "  if (name === 'state' || name === 'country') {"
+      "    var select_element = document.createElement('select');"
+      "    select_element.setAttribute('id', name);"
+      "    select_element.setAttribute('name', name);"
+      ""
+      "    /* Add an empty selected option. */"
+      "    var default_option = new Option('--', '', true);"
+      "    select_element.appendChild(default_option);"
+      ""
+      "    /* Add the other options. */"
+      "    if (name == 'state') {"
+      "      var option1 = new Option('California', 'CA');"
+      "      select_element.appendChild(option1);"
+      "      var option2 = new Option('Texas', 'TX');"
+      "      select_element.appendChild(option2);"
+      "    } else {"
+      "      var option1 = new Option('Canada', 'CA');"
+      "      select_element.appendChild(option1);"
+      "      var option2 = new Option('United States', 'US');"
+      "      select_element.appendChild(option2);"
+      "    }"
+      ""
+      "    form.appendChild(select_element);"
+      "  } else {"
+      "    var input_element = document.createElement('input');"
+      "    input_element.setAttribute('id', name);"
+      "    input_element.setAttribute('name', name);"
+      ""
+      "    /* Add the onfocus listener to the 'firstname' field. */"
+      "    if (name === 'firstname') {"
+      "      input_element.onfocus = function() {"
+      "        domAutomationController.send(true);"
+      "      };"
+      "    }"
+      ""
+      "    form.appendChild(input_element);"
+      "  }"
+      ""
+      "  form.appendChild(document.createElement('br'));"
+      "};"
+      ""
+      "function BuildForm() {"
+      "  var elements = ["
+      "    ['firstname', 'First name:'],"
+      "    ['lastname', 'Last name:'],"
+      "    ['address1', 'Address line 1:'],"
+      "    ['address2', 'Address line 2:'],"
+      "    ['city', 'City:'],"
+      "    ['state', 'State:'],"
+      "    ['zip', 'ZIP code:'],"
+      "    ['country', 'Country:'],"
+      "    ['phone', 'Phone number:'],"
+      "  ];"
+      ""
+      "  for (var i = 0; i < elements.length; i++) {"
+      "    var name = elements[i][0];"
+      "    var label = elements[i][1];"
+      "    AddElement(name, label);"
+      "  }"
+      "};"
+      "</script>";
+
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
-      GURL(std::string(kDataURIPrefix) +
-           "<form id=\"form\" action=\"http://www.example.com/\""
-           "      method=\"POST\"></form>"
-           "<script>"
-           "function AddElement(name, label) {"
-           "  var form = document.getElementById('form');"
-           ""
-           "  var label_text = document.createTextNode(label);"
-           "  var label_element = document.createElement('label');"
-           "  label_element.setAttribute('for', name);"
-           "  label_element.appendChild(label_text);"
-           "  form.appendChild(label_element);"
-           ""
-           "  if (name === 'state' || name === 'country') {"
-           "    var select_element = document.createElement('select');"
-           "    select_element.setAttribute('id', name);"
-           "    select_element.setAttribute('name', name);"
-           ""
-           "    /* Add an empty selected option. */"
-           "    var default_option = new Option('--', '', true);"
-           "    select_element.appendChild(default_option);"
-           ""
-           "    /* Add the other options. */"
-           "    if (name == 'state') {"
-           "      var option1 = new Option('California', 'CA');"
-           "      select_element.appendChild(option1);"
-           "      var option2 = new Option('Texas', 'TX');"
-           "      select_element.appendChild(option2);"
-           "    } else {"
-           "      var option1 = new Option('Canada', 'CA');"
-           "      select_element.appendChild(option1);"
-           "      var option2 = new Option('United States', 'US');"
-           "      select_element.appendChild(option2);"
-           "    }"
-           ""
-           "    form.appendChild(select_element);"
-           "  } else {"
-           "    var input_element = document.createElement('input');"
-           "    input_element.setAttribute('id', name);"
-           "    input_element.setAttribute('name', name);"
-           ""
-           "    /* Add the onfocus listener to the 'firstname' field. */"
-           "    if (name === 'firstname') {"
-           "      input_element.onfocus = function() {"
-           "        domAutomationController.send(true);"
-           "      };"
-           "    }"
-           ""
-           "    form.appendChild(input_element);"
-           "  }"
-           ""
-           "  form.appendChild(document.createElement('br'));"
-           "};"
-           ""
-           "function BuildForm() {"
-           "  var elements = ["
-           "    ['firstname', 'First name:'],"
-           "    ['lastname', 'Last name:'],"
-           "    ['address1', 'Address line 1:'],"
-           "    ['address2', 'Address line 2:'],"
-           "    ['city', 'City:'],"
-           "    ['state', 'State:'],"
-           "    ['zip', 'ZIP code:'],"
-           "    ['country', 'Country:'],"
-           "    ['phone', 'Phone number:'],"
-           "  ];"
-           ""
-           "  for (var i = 0; i < elements.length; i++) {"
-           "    var name = elements[i][0];"
-           "    var label = elements[i][1];"
-           "    AddElement(name, label);"
-           "  }"
-           "};"
-           "</script>")));
+  SetTestUrlResponse(kDynamicForm);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Dynamically construct the form.
   ASSERT_TRUE(content::ExecuteScript(GetWebContents(), "BuildForm();"));
@@ -1626,8 +1689,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Reload the page.
   content::WebContents* web_contents = GetWebContents();
@@ -1644,8 +1708,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestEventFormString)));
+  SetTestUrlResponse(kTestEventFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill.
   TryBasicFormFill();
@@ -1750,49 +1815,51 @@
 
   CreateTestProfile();
 
-  GURL url(std::string(kDataURIPrefix) +
-               "<form action=\"http://www.example.com/\" method=\"POST\">"
-               "<label for=\"fn\">なまえ</label>"
-               " <input type=\"text\" id=\"fn\""
-               "        onfocus=\"domAutomationController.send(true)\""
-               "><br>"
-               "<label for=\"ln\">みょうじ</label>"
-               " <input type=\"text\" id=\"ln\"><br>"
-               "<label for=\"a1\">Address line 1:</label>"
-               " <input type=\"text\" id=\"a1\"><br>"
-               "<label for=\"a2\">Address line 2:</label>"
-               " <input type=\"text\" id=\"a2\"><br>"
-               "<label for=\"ci\">City:</label>"
-               " <input type=\"text\" id=\"ci\"><br>"
-               "<label for=\"st\">State:</label>"
-               " <select id=\"st\">"
-               " <option value=\"\" selected=\"yes\">--</option>"
-               " <option value=\"CA\">California</option>"
-               " <option value=\"TX\">Texas</option>"
-               " </select><br>"
-               "<label for=\"z\">ZIP code:</label>"
-               " <input type=\"text\" id=\"z\"><br>"
-               "<label for=\"co\">Country:</label>"
-               " <select id=\"co\">"
-               " <option value=\"\" selected=\"yes\">--</option>"
-               " <option value=\"CA\">Canada</option>"
-               " <option value=\"US\">United States</option>"
-               " </select><br>"
-               "<label for=\"ph\">Phone number:</label>"
-               " <input type=\"text\" id=\"ph\"><br>"
-               "</form>"
-               // Add additional Japanese characters to ensure the translate bar
-               // will appear.
-               "我々は重要な、興味深いものになるが、時折状況が発生するため苦労や痛みは"
-               "彼にいくつかの素晴らしいを調達することができます。それから、いくつかの利");
+  static const char kForm[] =
+      "<form action=\"http://www.example.com/\" method=\"POST\">"
+      "<label for=\"fn\">なまえ</label>"
+      " <input type=\"text\" id=\"fn\""
+      "        onfocus=\"domAutomationController.send(true)\""
+      "><br>"
+      "<label for=\"ln\">みょうじ</label>"
+      " <input type=\"text\" id=\"ln\"><br>"
+      "<label for=\"a1\">Address line 1:</label>"
+      " <input type=\"text\" id=\"a1\"><br>"
+      "<label for=\"a2\">Address line 2:</label>"
+      " <input type=\"text\" id=\"a2\"><br>"
+      "<label for=\"ci\">City:</label>"
+      " <input type=\"text\" id=\"ci\"><br>"
+      "<label for=\"st\">State:</label>"
+      " <select id=\"st\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">California</option>"
+      " <option value=\"TX\">Texas</option>"
+      " </select><br>"
+      "<label for=\"z\">ZIP code:</label>"
+      " <input type=\"text\" id=\"z\"><br>"
+      "<label for=\"co\">Country:</label>"
+      " <select id=\"co\">"
+      " <option value=\"\" selected=\"yes\">--</option>"
+      " <option value=\"CA\">Canada</option>"
+      " <option value=\"US\">United States</option>"
+      " </select><br>"
+      "<label for=\"ph\">Phone number:</label>"
+      " <input type=\"text\" id=\"ph\"><br>"
+      "</form>"
+      // Add additional Japanese characters to ensure the translate bar
+      // will appear.
+      "我々は重要な、興味深いものになるが、時折状況が発生するため苦労や痛みは"
+      "彼にいくつかの素晴らしいを調達することができます。それから、いくつかの"
+      "利";
 
   // Set up an observer to be able to wait for the bubble to be shown.
   content::Source<content::WebContents> source(GetWebContents());
   content::WindowedNotificationObserver language_detected_signal(
       chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, source);
 
+  SetTestUrlResponse(kForm);
   ASSERT_NO_FATAL_FAILURE(
-      ui_test_utils::NavigateToURL(browser(), url));
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Wait for the translate bubble to appear.
   language_detected_signal.Wait();
@@ -2054,8 +2121,9 @@
   CreateTestProfile();
 
   // Load the test page.
-  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
-      browser(), GURL(std::string(kDataURIPrefix) + kTestShippingFormString)));
+  SetTestUrlResponse(kTestShippingFormString);
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
 
   // Invoke Autofill: Start filling the first name field with "M" and wait for
   // the popup to be shown.
diff --git a/chrome/browser/autofill/autofill_server_browsertest.cc b/chrome/browser/autofill/autofill_server_browsertest.cc
index 56cae23b..c043036 100644
--- a/chrome/browser/autofill/autofill_server_browsertest.cc
+++ b/chrome/browser/autofill/autofill_server_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -18,6 +19,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
@@ -124,11 +126,27 @@
 
 class AutofillServerTest : public InProcessBrowserTest  {
  public:
+  void SetUp() override {
+    // Enable data-url support.
+    // TODO(crbug.com/894428) - fix this suite to use the embedded test server
+    // instead of data urls.
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kAutofillAllowNonHttpActivation);
+
+    // Note that features MUST be enabled/disabled before continuing with
+    // SetUp(); otherwise, the feature state doesn't propagate to the test
+    // browser instance.
+    InProcessBrowserTest::SetUp();
+  }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Enable finch experiment for sending field metadata.
     command_line->AppendSwitchASCII(
         ::switches::kForceFieldTrials, "AutofillFieldMetadata/Enabled/");
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Regression test for http://crbug.com/177419
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 5468dbb9..57e325b 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -108,9 +108,12 @@
 }
 
 void BrowserProcessPlatformPart::InitializeCrosComponentManager() {
+  if (using_testing_cros_component_manager_)
+    return;
+
   DCHECK(!cros_component_manager_);
   cros_component_manager_ =
-      std::make_unique<component_updater::CrOSComponentManager>(
+      std::make_unique<component_updater::CrOSComponentInstaller>(
           component_updater::MetadataTable::Create(
               g_browser_process->local_state()));
 
@@ -119,6 +122,9 @@
 }
 
 void BrowserProcessPlatformPart::ShutdownCrosComponentManager() {
+  if (using_testing_cros_component_manager_)
+    return;
+
   cros_component_manager_.reset();
 }
 
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index 692a4c48..b2f616d 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -13,6 +13,8 @@
 #include "base/sequence_checker.h"
 #include "chrome/browser/browser_process_platform_part_base.h"
 
+class BrowserProcessPlatformPartTestApi;
+
 namespace chromeos {
 class AccountManagerFactory;
 class ChromeSessionManager;
@@ -121,6 +123,8 @@
   chromeos::AccountManagerFactory* GetAccountManagerFactory();
 
  private:
+  friend class BrowserProcessPlatformPartTestApi;
+
   void CreateProfileHelper();
 
   std::unique_ptr<chromeos::ChromeSessionManager> session_manager_;
@@ -146,6 +150,9 @@
 
   std::unique_ptr<ScopedKeepAlive> keep_alive_;
 
+  // Whether cros_component_manager_ has been initialized for test. Set by
+  // BrowserProcessPlatformPartTestApi.
+  bool using_testing_cros_component_manager_ = false;
   std::unique_ptr<component_updater::CrOSComponentManager>
       cros_component_manager_;
 
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h
index 186621ce..959baeb 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h
@@ -42,6 +42,8 @@
   virtual void SendFocus(mojom::InputConnectionPtr connection,
                          mojom::TextInputStatePtr state) = 0;
   virtual void SendUpdateTextInputState(mojom::TextInputStatePtr state) = 0;
+  virtual void SendShowVirtualKeyboard() = 0;
+  virtual void SendHideVirtualKeyboard() = 0;
 };
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.cc
index 512e7ed..a7e4bd87 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.cc
@@ -81,6 +81,30 @@
   imm_instance->UpdateTextInputState(std::move(state));
 }
 
+void ArcInputMethodManagerBridgeImpl::SendShowVirtualKeyboard() {
+  auto* imm_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      bridge_service_->input_method_manager(), ShowVirtualKeyboard);
+  if (!imm_instance)
+    return;
+
+  if (!base::FeatureList::IsEnabled(kEnableInputMethodFeature))
+    return;
+
+  imm_instance->ShowVirtualKeyboard();
+}
+
+void ArcInputMethodManagerBridgeImpl::SendHideVirtualKeyboard() {
+  auto* imm_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      bridge_service_->input_method_manager(), HideVirtualKeyboard);
+  if (!imm_instance)
+    return;
+
+  if (!base::FeatureList::IsEnabled(kEnableInputMethodFeature))
+    return;
+
+  imm_instance->HideVirtualKeyboard();
+}
+
 void ArcInputMethodManagerBridgeImpl::OnConnectionClosed() {
   delegate_->OnConnectionClosed();
 }
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.h b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.h
index fa817c6d..ffcf826 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.h
@@ -34,6 +34,8 @@
   void SendFocus(mojom::InputConnectionPtr connection,
                  mojom::TextInputStatePtr state) override;
   void SendUpdateTextInputState(mojom::TextInputStatePtr state) override;
+  void SendShowVirtualKeyboard() override;
+  void SendHideVirtualKeyboard() override;
 
   // ConnectionObserver<mojom::InputMethodManagerInstance> overrides:
   void OnConnectionClosed() override;
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index 6fc830b..40658b5f 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -28,6 +28,7 @@
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
 #include "ui/base/ime/ime_bridge.h"
+#include "ui/base/ime/input_method_observer.h"
 #include "ui/keyboard/keyboard_util.h"
 
 namespace arc {
@@ -90,15 +91,16 @@
 
 }  // namespace
 
-class ArcInputMethodManagerService::ArcProxyInputMethodObserver
+class ArcInputMethodManagerService::InputMethodEngineObserver
     : public input_method::InputMethodEngineBase::Observer {
  public:
-  explicit ArcProxyInputMethodObserver(ArcInputMethodManagerService* owner)
+  explicit InputMethodEngineObserver(ArcInputMethodManagerService* owner)
       : owner_(owner) {}
-  ~ArcProxyInputMethodObserver() override = default;
+  ~InputMethodEngineObserver() override = default;
 
   // input_method::InputMethodEngineBase::Observer overrides:
   void OnActivate(const std::string& engine_id) override {
+    owner_->OnArcImeActivated();
     // ash::Shell is not created in the unit tests.
     if (!ash::Shell::HasInstance())
       return;
@@ -117,9 +119,18 @@
   void OnKeyEvent(
       const std::string& engine_id,
       const input_method::InputMethodEngineBase::KeyboardEvent& event,
-      ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override {}
+      ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override {
+    if (event.key == "HistoryBack") {
+      // Back button on the shelf is pressed.
+      owner_->imm_bridge_->SendHideVirtualKeyboard();
+      std::move(key_data).Run(true);
+      return;
+    }
+    std::move(key_data).Run(false);
+  }
   void OnReset(const std::string& engine_id) override {}
   void OnDeactivated(const std::string& engine_id) override {
+    owner_->OnArcImeDeactivated();
     // ash::Shell is not created in the unit tests.
     if (!ash::Shell::HasInstance())
       return;
@@ -135,7 +146,7 @@
       const std::vector<gfx::Rect>& bounds) override {
     owner_->UpdateTextInputState();
   }
-  bool IsInterestedInKeyEvent() const override { return false; }
+  bool IsInterestedInKeyEvent() const override { return true; }
   void OnSurroundingTextChanged(const std::string& engine_id,
                                 const std::string& text,
                                 int cursor_pos,
@@ -157,7 +168,30 @@
  private:
   ArcInputMethodManagerService* const owner_;
 
-  DISALLOW_COPY_AND_ASSIGN(ArcProxyInputMethodObserver);
+  DISALLOW_COPY_AND_ASSIGN(InputMethodEngineObserver);
+};
+
+class ArcInputMethodManagerService::InputMethodObserver
+    : public ui::InputMethodObserver {
+ public:
+  explicit InputMethodObserver(ArcInputMethodManagerService* owner)
+      : owner_(owner) {}
+  ~InputMethodObserver() override = default;
+
+  // ui::InputMethodObserver overrides:
+  void OnFocus() override {}
+  void OnBlur() override {}
+  void OnCaretBoundsChanged(const ui::TextInputClient* client) override {}
+  void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
+  void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
+  void OnShowVirtualKeyboardIfEnabled() override {
+    owner_->imm_bridge_->SendShowVirtualKeyboard();
+  }
+
+ private:
+  ArcInputMethodManagerService* const owner_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputMethodObserver);
 };
 
 class ArcInputMethodManagerService::TabletModeObserver
@@ -203,13 +237,14 @@
       proxy_ime_extension_id_(
           crx_file::id_util::GenerateId(kArcIMEProxyExtensionName)),
       proxy_ime_engine_(std::make_unique<chromeos::InputMethodEngine>()),
-      tablet_mode_observer_(std::make_unique<TabletModeObserver>(this)) {
+      tablet_mode_observer_(std::make_unique<TabletModeObserver>(this)),
+      input_method_observer_(std::make_unique<InputMethodObserver>(this)) {
   auto* imm = chromeos::input_method::InputMethodManager::Get();
   imm->AddObserver(this);
   imm->AddImeMenuObserver(this);
 
   proxy_ime_engine_->Initialize(
-      std::make_unique<ArcProxyInputMethodObserver>(this),
+      std::make_unique<InputMethodEngineObserver>(this),
       proxy_ime_extension_id_.c_str(), profile_);
 
   // TabletModeClient should be already created here because it's created in
@@ -586,4 +621,18 @@
     manager->NotifyInputMethodExtensionAdded(proxy_ime_extension_id_);
 }
 
+void ArcInputMethodManagerService::OnArcImeActivated() {
+  ui::InputMethod* input_method =
+      ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
+  if (input_method)
+    input_method->AddObserver(input_method_observer_.get());
+}
+
+void ArcInputMethodManagerService::OnArcImeDeactivated() {
+  ui::InputMethod* input_method =
+      ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
+  if (input_method)
+    input_method->RemoveObserver(input_method_observer_.get());
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
index 315cf3a..c0c3d5a 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
@@ -70,7 +70,8 @@
   InputConnectionImpl* GetInputConnectionForTesting();
 
  private:
-  class ArcProxyInputMethodObserver;
+  class InputMethodEngineObserver;
+  class InputMethodObserver;
   class TabletModeObserver;
 
   void EnableIme(const std::string& ime_id, bool enable);
@@ -95,6 +96,10 @@
   // Notifies InputMethodManager's observers of possible ARC IME state changes.
   void NotifyInputMethodManagerObservers(bool is_tablet_mode);
 
+  // Called by InputMethodEngineObserver.
+  void OnArcImeActivated();
+  void OnArcImeDeactivated();
+
   Profile* const profile_;
 
   std::unique_ptr<ArcInputMethodManagerBridge> imm_bridge_;
@@ -110,6 +115,8 @@
 
   std::unique_ptr<TabletModeObserver> tablet_mode_observer_;
 
+  std::unique_ptr<InputMethodObserver> input_method_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcInputMethodManagerService);
 };
 
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
index 98a1e23..81daec3 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
@@ -206,11 +206,17 @@
     last_text_input_state = state.Clone();
   }
 
+  void SendShowVirtualKeyboard() override {
+    ++show_virtual_keyboard_calls_count_;
+  }
+  void SendHideVirtualKeyboard() override {}
+
   std::vector<std::tuple<std::string, bool>> enable_ime_calls_;
   std::vector<std::string> switch_ime_to_calls_;
   int focus_calls_count_ = 0;
   int update_text_input_state_calls_count_ = 0;
   mojom::TextInputStatePtr last_text_input_state;
+  int show_virtual_keyboard_calls_count_ = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestInputMethodManagerBridge);
@@ -669,14 +675,8 @@
   ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
   ui::IMEEngineHandlerInterface* engine_handler =
       std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
-  // Enable it
-  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
-  engine_handler->Enable(
-      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
-          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
-              .at(0)
-              .id()));
 
+  // Set up mock input context.
   constexpr int test_context_id = 0;
   const ui::IMEEngineHandlerInterface::InputContext test_context{
       test_context_id,
@@ -687,8 +687,16 @@
       true /* should_do_learning */};
   ui::MockInputMethod mock_input_method(nullptr);
   TestIMEInputContextHandler test_context_handler(&mock_input_method);
-  ui::DummyTextInputClient dummy_text_input_client;
+  ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
   ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
+
+  // Enable the ARC IME.
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
+  engine_handler->Enable(
+      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
+          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
+              .at(0)
+              .id()));
   mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
 
   ASSERT_EQ(0, bridge()->focus_calls_count_);
@@ -737,14 +745,8 @@
   ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
   ui::IMEEngineHandlerInterface* engine_handler =
       std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
-  // Enable it
-  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
-  engine_handler->Enable(
-      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
-          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
-              .at(0)
-              .id()));
 
+  // Set up mock input context.
   constexpr int test_context_id = 0;
   const ui::IMEEngineHandlerInterface::InputContext test_context{
       test_context_id,
@@ -757,6 +759,14 @@
   TestIMEInputContextHandler test_context_handler(&mock_input_method);
   ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
   ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
+
+  // Enable the ARC IME.
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
+  engine_handler->Enable(
+      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
+          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
+              .at(0)
+              .id()));
   mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
 
   engine_handler->FocusIn(test_context);
@@ -880,4 +890,60 @@
   EXPECT_TRUE(keyboard::IsKeyboardEnabled());
 }
 
+TEST_F(ArcInputMethodManagerServiceTest, ShowVirtualKeyboard) {
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(kEnableInputMethodFeature);
+  ToggleTabletMode(true);
+
+  // Adding one ARC IME.
+  {
+    const std::string android_ime_id = "test.arc.ime";
+    const std::string display_name = "DisplayName";
+    const std::string settings_url = "url_to_settings";
+    mojom::ImeInfoPtr info = mojom::ImeInfo::New();
+    info->ime_id = android_ime_id;
+    info->display_name = display_name;
+    info->enabled = false;
+    info->settings_url = settings_url;
+
+    std::vector<mojom::ImeInfoPtr> info_array;
+    info_array.emplace_back(std::move(info));
+    service()->OnImeInfoChanged(std::move(info_array));
+  }
+  // The proxy IME engine should be added.
+  ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
+  ui::IMEEngineHandlerInterface* engine_handler =
+      std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
+
+  // Set up mock input context.
+  constexpr int test_context_id = 0;
+  const ui::IMEEngineHandlerInterface::InputContext test_context{
+      test_context_id,
+      ui::TEXT_INPUT_TYPE_TEXT,
+      ui::TEXT_INPUT_MODE_DEFAULT,
+      0 /* flags */,
+      ui::TextInputClient::FOCUS_REASON_MOUSE,
+      true /* should_do_learning */};
+  ui::MockInputMethod mock_input_method(nullptr);
+  TestIMEInputContextHandler test_context_handler(&mock_input_method);
+  ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+  ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
+
+  // Enable the ARC IME.
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
+  engine_handler->Enable(
+      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
+          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
+              .at(0)
+              .id()));
+
+  mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
+
+  EXPECT_EQ(0, bridge()->show_virtual_keyboard_calls_count_);
+  mock_input_method.ShowVirtualKeyboardIfEnabled();
+  EXPECT_EQ(1, bridge()->show_virtual_keyboard_calls_count_);
+  ui::IMEBridge::Get()->SetInputContextHandler(nullptr);
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader_unittest.cc
index 7b427fe..f51cf5d 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader_unittest.cc
@@ -22,14 +22,17 @@
 #include "base/run_loop.h"
 #include "base/values.h"
 #include "base/version.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/component_updater/fake_cros_component_manager.h"
 #include "chrome/browser/extensions/external_provider_impl.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/fake_image_loader_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/browser/notification_service.h"
@@ -140,21 +143,19 @@
 class DemoExtensionsExternalLoaderTest : public testing::Test {
  public:
   DemoExtensionsExternalLoaderTest()
-      : test_shared_loader_factory_(
+      : browser_process_platform_part_test_api_(
+            TestingBrowserProcess::GetGlobal()->platform_part()),
+        test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {}
 
   ~DemoExtensionsExternalLoaderTest() override = default;
 
   void SetUp() override {
+    DBusThreadManager::Initialize();
     DemoSession::SetDemoConfigForTesting(DemoSession::DemoModeConfig::kOnline);
 
     ASSERT_TRUE(offline_demo_resources_.CreateUniqueTempDir());
-
-    auto image_loader_client = std::make_unique<FakeImageLoaderClient>();
-    image_loader_client_ = image_loader_client.get();
-    DBusThreadManager::GetSetterForTesting()->SetImageLoaderClient(
-        std::move(image_loader_client));
     session_manager_ = std::make_unique<session_manager::SessionManager>();
 
     TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
@@ -163,22 +164,17 @@
 
   void TearDown() override {
     profile_.reset();
-
-    image_loader_client_ = nullptr;
     DBusThreadManager::Shutdown();
-
     DemoSession::ShutDownIfInitialized();
     DemoSession::ResetDemoConfigForTesting();
+    browser_process_platform_part_test_api_.ShutdownCrosComponentManager();
   }
 
  protected:
   void InitializeSession(bool mount_demo_resources,
                          bool wait_for_offline_resources_load) {
-    if (mount_demo_resources) {
-      image_loader_client_->SetMountPathForComponent(
-          DemoSession::kDemoModeResourcesComponentName,
-          offline_demo_resources_.GetPath());
-    }
+    InitializeCrosComponentManager(mount_demo_resources);
+
     ASSERT_TRUE(DemoSession::StartIfInDemoMode());
 
     if (wait_for_offline_resources_load)
@@ -187,6 +183,23 @@
     profile_ = std::make_unique<TestingProfile>();
   }
 
+  void InitializeCrosComponentManager(bool enable_demo_resources) {
+    auto cros_component_manager =
+        std::make_unique<component_updater::FakeCrOSComponentManager>();
+    cros_component_manager->set_supported_components(
+        {DemoSession::kDemoModeResourcesComponentName});
+    if (enable_demo_resources) {
+      cros_component_manager->ResetComponentState(
+          DemoSession::kDemoModeResourcesComponentName,
+          component_updater::FakeCrOSComponentManager::ComponentInfo(
+              component_updater::CrOSComponentManager::Error::NONE,
+              base::FilePath("/dev/null"), offline_demo_resources_.GetPath()));
+    }
+
+    browser_process_platform_part_test_api_.InitializeCrosComponentManager(
+        std::move(cros_component_manager));
+  }
+
   void WaitForOfflineResourcesLoad() {
     base::RunLoop run_loop;
     DemoSession::Get()->EnsureOfflineResourcesLoaded(run_loop.QuitClosure());
@@ -249,8 +262,7 @@
  private:
   content::TestBrowserThreadBundle thread_bundle_;
 
-  // Image loader client injected into, and owned by DBusThreadManager.
-  FakeImageLoaderClient* image_loader_client_ = nullptr;
+  BrowserProcessPlatformPartTestApi browser_process_platform_part_test_api_;
 
   base::ScopedTempDir offline_demo_resources_;
 
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
index 45d426c..80de2a5 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
@@ -14,12 +14,17 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/component_updater/fake_cros_component_manager.h"
+#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/fake_image_loader_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using component_updater::FakeCrOSComponentManager;
+
 namespace chromeos {
 
 namespace {
@@ -36,110 +41,65 @@
   *value = true;
 }
 
-class TestImageLoaderClient : public FakeImageLoaderClient {
- public:
-  TestImageLoaderClient() = default;
-  ~TestImageLoaderClient() override = default;
-
-  const std::list<std::string>& pending_loads() const { return pending_loads_; }
-
-  // FakeImageLoaderClient:
-  void LoadComponentAtPath(
-      const std::string& name,
-      const base::FilePath& path,
-      DBusMethodCallback<base::FilePath> callback) override {
-    ASSERT_FALSE(path.empty());
-
-    components_[name].source = path;
-    components_[name].load_callbacks.emplace_back(std::move(callback));
-    pending_loads_.push_back(name);
-  }
-
-  bool FinishComponentLoad(const std::string& component_name,
-                           const base::FilePath& mount_point) {
-    if (pending_loads_.empty() || pending_loads_.front() != component_name)
-      return false;
-    pending_loads_.pop_front();
-
-    components_[component_name].loaded = true;
-    RunPendingLoadCallback(component_name, mount_point);
-    return true;
-  }
-
-  bool FailComponentLoad(const std::string& component_name) {
-    if (pending_loads_.empty() || pending_loads_.front() != component_name)
-      return false;
-    pending_loads_.pop_front();
-    RunPendingLoadCallback(component_name, base::nullopt);
-    return true;
-  }
-
-  bool ComponentLoadedFromPath(const std::string& name,
-                               const base::FilePath& file_path) const {
-    const auto& component = components_.find(name);
-    if (component == components_.end())
-      return false;
-    return component->second.loaded && component->second.source == file_path;
-  }
-
- private:
-  struct ComponentInfo {
-    ComponentInfo() = default;
-    ~ComponentInfo() = default;
-
-    base::FilePath source;
-    bool loaded = false;
-    std::list<DBusMethodCallback<base::FilePath>> load_callbacks;
-  };
-
-  void RunPendingLoadCallback(const std::string& component_name,
-                              base::Optional<base::FilePath> mount_point) {
-    DBusMethodCallback<base::FilePath> callback =
-        std::move(components_[component_name].load_callbacks.front());
-    components_[component_name].load_callbacks.pop_front();
-
-    std::move(callback).Run(std::move(mount_point));
-  }
-
-  // Map containing known components.
-  std::map<std::string, ComponentInfo> components_;
-
-  // List of components whose load has been requested.
-  std::list<std::string> pending_loads_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestImageLoaderClient);
-};
-
 }  // namespace
 
 class DemoSessionTest : public testing::Test {
  public:
-  DemoSessionTest() = default;
+  DemoSessionTest()
+      : browser_process_platform_part_test_api_(
+            g_browser_process->platform_part()) {}
   ~DemoSessionTest() override = default;
 
   void SetUp() override {
+    chromeos::DBusThreadManager::Initialize();
     DemoSession::SetDemoConfigForTesting(DemoSession::DemoModeConfig::kOnline);
-    auto image_loader_client = std::make_unique<TestImageLoaderClient>();
-    image_loader_client_ = image_loader_client.get();
-    chromeos::DBusThreadManager::GetSetterForTesting()->SetImageLoaderClient(
-        std::move(image_loader_client));
+    InitializeCrosComponentManager();
     session_manager_ = std::make_unique<session_manager::SessionManager>();
   }
 
   void TearDown() override {
     DemoSession::ShutDownIfInitialized();
     DemoSession::ResetDemoConfigForTesting();
-    image_loader_client_ = nullptr;
+
     chromeos::DBusThreadManager::Shutdown();
+
+    cros_component_manager_ = nullptr;
+    browser_process_platform_part_test_api_.ShutdownCrosComponentManager();
   }
 
  protected:
-  // Points to the image loader client passed to the test DBusTestManager.
-  TestImageLoaderClient* image_loader_client_ = nullptr;
+  bool FinishResourcesComponentLoad(const base::FilePath& mount_path) {
+    EXPECT_TRUE(
+        cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
+    EXPECT_FALSE(
+        cros_component_manager_->UpdateRequested(kOfflineResourcesComponent));
+
+    return cros_component_manager_->FinishLoadRequest(
+        kOfflineResourcesComponent,
+        FakeCrOSComponentManager::ComponentInfo(
+            component_updater::CrOSComponentManager::Error::NONE,
+            base::FilePath("/dev/null"), mount_path));
+  }
+
+  void InitializeCrosComponentManager() {
+    auto fake_cros_component_manager =
+        std::make_unique<FakeCrOSComponentManager>();
+    fake_cros_component_manager->set_queue_load_requests(true);
+    fake_cros_component_manager->set_supported_components(
+        {kOfflineResourcesComponent});
+    cros_component_manager_ = fake_cros_component_manager.get();
+
+    browser_process_platform_part_test_api_.InitializeCrosComponentManager(
+        std::move(fake_cros_component_manager));
+  }
+
+  FakeCrOSComponentManager* cros_component_manager_ = nullptr;
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<session_manager::SessionManager> session_manager_;
 
  private:
+  BrowserProcessPlatformPartTestApi browser_process_platform_part_test_api_;
+
   DISALLOW_COPY_AND_ASSIGN(DemoSessionTest);
 };
 
@@ -157,13 +117,10 @@
   ASSERT_TRUE(demo_session);
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
 
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
 
   EXPECT_TRUE(demo_session->offline_resources_loaded());
   EXPECT_EQ(component_mount_point.AppendASCII(kDemoAppsImageFile),
@@ -193,7 +150,8 @@
   EXPECT_FALSE(DemoSession::StartIfInDemoMode());
   EXPECT_FALSE(DemoSession::Get());
 
-  EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 }
 
 TEST_F(DemoSessionTest, StartIfInOfflineEnrolledDemoMode) {
@@ -207,8 +165,8 @@
   EXPECT_EQ(demo_session, DemoSession::Get());
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 }
 
 TEST_F(DemoSessionTest, PreloadOfflineResourcesIfInDemoMode) {
@@ -220,13 +178,12 @@
   EXPECT_FALSE(demo_session->offline_enrolled());
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
 
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_FALSE(demo_session->started());
   EXPECT_TRUE(demo_session->offline_resources_loaded());
@@ -240,7 +197,8 @@
   DemoSession::SetDemoConfigForTesting(DemoSession::DemoModeConfig::kNone);
   DemoSession::PreloadOfflineResourcesIfInDemoMode();
   EXPECT_FALSE(DemoSession::Get());
-  EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 }
 
 TEST_F(DemoSessionTest, PreloadOfflineResourcesIfInOfflineDemoMode) {
@@ -253,8 +211,8 @@
   EXPECT_TRUE(demo_session->offline_enrolled());
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 }
 
 TEST_F(DemoSessionTest, ShutdownResetsInstance) {
@@ -279,13 +237,12 @@
   EXPECT_TRUE(demo_session->started());
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
 
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_TRUE(demo_session->started());
   EXPECT_TRUE(demo_session->offline_resources_loaded());
@@ -298,13 +255,11 @@
 TEST_F(DemoSessionTest, StartDemoSessionAfterPreloadingResources) {
   DemoSession::PreloadOfflineResourcesIfInDemoMode();
 
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
-
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   DemoSession* demo_session = DemoSession::StartIfInDemoMode();
   EXPECT_TRUE(demo_session->started());
@@ -314,7 +269,8 @@
   EXPECT_EQ(component_mount_point.AppendASCII(kExternalExtensionsPrefsFile),
             demo_session->GetExternalExtensionsPrefsPath());
 
-  EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 }
 
 TEST_F(DemoSessionTest, EnsureOfflineResourcesLoadedAfterStart) {
@@ -328,13 +284,11 @@
   EXPECT_FALSE(callback_called);
   EXPECT_FALSE(demo_session->offline_resources_loaded());
 
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
-
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_TRUE(callback_called);
   EXPECT_TRUE(demo_session->offline_resources_loaded());
@@ -347,18 +301,18 @@
 TEST_F(DemoSessionTest, EnsureOfflineResourcesLoadedAfterOfflineResourceLoad) {
   DemoSession* demo_session = DemoSession::StartIfInDemoMode();
   ASSERT_TRUE(demo_session);
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
 
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   bool callback_called = false;
   demo_session->EnsureOfflineResourcesLoaded(
       base::BindOnce(&SetBoolean, &callback_called));
-  EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_TRUE(callback_called);
   EXPECT_TRUE(demo_session->offline_resources_loaded());
@@ -381,13 +335,11 @@
   EXPECT_FALSE(callback_called);
   EXPECT_FALSE(demo_session->offline_resources_loaded());
 
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
-
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_TRUE(callback_called);
   EXPECT_TRUE(demo_session->offline_resources_loaded());
@@ -418,13 +370,11 @@
   EXPECT_FALSE(third_callback_called);
   EXPECT_FALSE(demo_session->offline_resources_loaded());
 
-  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
-            image_loader_client_->pending_loads());
-
   const base::FilePath component_mount_point =
       base::FilePath(kTestDemoModeResourcesMountPoint);
-  ASSERT_TRUE(image_loader_client_->FinishComponentLoad(
-      kOfflineResourcesComponent, component_mount_point));
+  ASSERT_TRUE(FinishResourcesComponentLoad(component_mount_point));
+  EXPECT_FALSE(
+      cros_component_manager_->HasPendingInstall(kOfflineResourcesComponent));
 
   EXPECT_TRUE(first_callback_called);
   EXPECT_TRUE(second_callback_called);
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index 6772988..a92a59f 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -346,7 +346,8 @@
 bool EasyUnlockServiceSignin::IsAllowedInternal() const {
   return service_active_ && account_id_.is_valid() &&
          !LoginState::Get()->IsUserLoggedIn() &&
-         (pref_manager_ && pref_manager_->IsEasyUnlockAllowed());
+         (pref_manager_ && pref_manager_->IsEasyUnlockAllowed() &&
+          pref_manager_->IsChromeOSLoginAllowed());
 }
 
 bool EasyUnlockServiceSignin::IsEnabled() const {
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index d18e5483..db38043 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -2393,8 +2393,9 @@
   DISALLOW_COPY_AND_ASSIGN(WizardControllerDemoSetupDeviceDisabledTest);
 };
 
+// Flaky https://crbug.com/894384.
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupDeviceDisabledTest,
-                       OnlineDemoSetup) {
+                       DISABLED_OnlineDemoSetup) {
   CheckCurrentScreen(OobeScreen::SCREEN_OOBE_WELCOME);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
   WaitUntilJSIsReady();
diff --git a/chrome/browser/chromeos/smb_client/smb_service.cc b/chrome/browser/chromeos/smb_client/smb_service.cc
index 9a01a0b..b889255 100644
--- a/chrome/browser/chromeos/smb_client/smb_service.cc
+++ b/chrome/browser/chromeos/smb_client/smb_service.cc
@@ -121,6 +121,7 @@
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(prefs::kNetworkFileSharesAllowed, true);
   registry->RegisterBooleanPref(prefs::kNetBiosShareDiscoveryEnabled, true);
+  registry->RegisterBooleanPref(prefs::kNTLMShareAuthenticationEnabled, true);
 }
 
 void SmbService::Mount(const file_system_provider::MountOptions& options,
@@ -187,7 +188,13 @@
     return;
   }
 
-  const base::FilePath mount_path(share_finder_->GetResolvedUrl(parsed_url));
+  // If using kerberos, the hostname should not be resolved since kerberos
+  // service tickets are keyed on hosname.
+  const base::FilePath mount_path =
+      use_chromad_kerberos
+          ? share_path
+          : base::FilePath(share_finder_->GetResolvedUrl(parsed_url));
+
   GetSmbProviderClient()->Mount(
       mount_path, workgroup, username,
       temp_file_manager_->WritePasswordToFile(password),
@@ -292,7 +299,12 @@
     return;
   }
 
-  const base::FilePath mount_path(share_finder_->GetResolvedUrl(parsed_url));
+  // If using kerberos, the hostname should not be resolved since kerberos
+  // service tickets are keyed on hosname.
+  const base::FilePath mount_path =
+      is_kerberos_chromad
+          ? share_path
+          : base::FilePath(share_finder_->GetResolvedUrl(parsed_url));
 
   // An empty password is passed to Remount to conform with the credentials API
   // which expects username & workgroup strings along with a password file
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index 851ad68..52c4216 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -104,8 +104,11 @@
 }  // namespace
 
 CrOSComponentInstallerPolicy::CrOSComponentInstallerPolicy(
-    const ComponentConfig& config)
-    : name_(config.name), env_version_(config.env_version) {
+    const ComponentConfig& config,
+    CrOSComponentInstaller* cros_component_installer)
+    : cros_component_installer_(cros_component_installer),
+      name_(config.name),
+      env_version_(config.env_version) {
   if (strlen(config.sha2hash) != crypto::kSHA256Length * 2)
     return;
 
@@ -132,17 +135,13 @@
   // TODO(xiaochu): remove after M66 ships to stable. https://crbug.com/792203
   CleanUpOldInstalls(name_);
 
-  g_browser_process->platform_part()
-      ->cros_component_manager()
-      ->EmitInstalledSignal(GetName());
+  cros_component_installer_->EmitInstalledSignal(GetName());
 
   return update_client::CrxInstaller::Result(update_client::InstallError::NONE);
 }
 
 void CrOSComponentInstallerPolicy::OnCustomUninstall() {
-  g_browser_process->platform_part()
-      ->cros_component_manager()
-      ->UnregisterCompatiblePath(name_);
+  cros_component_installer_->UnregisterCompatiblePath(name_);
 
   chromeos::DBusThreadManager::Get()->GetImageLoaderClient()->UnmountComponent(
       name_, base::BindOnce(&LogCustomUninstall));
@@ -159,9 +158,7 @@
   if (!IsCompatible(env_version_, min_env_version))
     return;
 
-  g_browser_process->platform_part()
-      ->cros_component_manager()
-      ->RegisterCompatiblePath(GetName(), path);
+  cros_component_installer_->RegisterCompatiblePath(GetName(), path);
 }
 
 bool CrOSComponentInstallerPolicy::VerifyInstallation(
@@ -205,20 +202,20 @@
          env_version >= min_env_version;
 }
 
-CrOSComponentManager::CrOSComponentManager(
+CrOSComponentInstaller::CrOSComponentInstaller(
     std::unique_ptr<MetadataTable> metadata_table)
     : metadata_table_(std::move(metadata_table)) {}
 
-CrOSComponentManager::~CrOSComponentManager() {}
+CrOSComponentInstaller::~CrOSComponentInstaller() {}
 
-void CrOSComponentManager::SetDelegate(Delegate* delegate) {
+void CrOSComponentInstaller::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
 }
 
-void CrOSComponentManager::Load(const std::string& name,
-                                MountPolicy mount_policy,
-                                UpdatePolicy update_policy,
-                                LoadCallback load_callback) {
+void CrOSComponentInstaller::Load(const std::string& name,
+                                  MountPolicy mount_policy,
+                                  UpdatePolicy update_policy,
+                                  LoadCallback load_callback) {
   if (!IsCompatible(name) || update_policy == UpdatePolicy::kForce) {
     // A compatible component is not installed, or forced update is requested.
     // Start registration and installation/update process.
@@ -235,7 +232,7 @@
   }
 }
 
-bool CrOSComponentManager::Unload(const std::string& name) {
+bool CrOSComponentInstaller::Unload(const std::string& name) {
   const ComponentConfig* config = FindConfig(name);
   if (!config) {
     // Component |name| does not exist.
@@ -248,33 +245,35 @@
          updater->UnregisterComponent(id);
 }
 
-void CrOSComponentManager::RegisterInstalled() {
+void CrOSComponentInstaller::RegisterInstalled() {
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, {base::MayBlock()}, base::BindOnce(GetInstalled),
-      base::BindOnce(&CrOSComponentManager::RegisterN, base::Unretained(this)));
+      base::BindOnce(&CrOSComponentInstaller::RegisterN,
+                     base::Unretained(this)));
 }
 
-void CrOSComponentManager::RegisterCompatiblePath(const std::string& name,
-                                                  const base::FilePath& path) {
+void CrOSComponentInstaller::RegisterCompatiblePath(
+    const std::string& name,
+    const base::FilePath& path) {
   compatible_components_[name] = path;
 }
 
-void CrOSComponentManager::UnregisterCompatiblePath(const std::string& name) {
+void CrOSComponentInstaller::UnregisterCompatiblePath(const std::string& name) {
   compatible_components_.erase(name);
 }
 
-base::FilePath CrOSComponentManager::GetCompatiblePath(
+base::FilePath CrOSComponentInstaller::GetCompatiblePath(
     const std::string& name) const {
   const auto it = compatible_components_.find(name);
   return it == compatible_components_.end() ? base::FilePath() : it->second;
 }
 
-void CrOSComponentManager::EmitInstalledSignal(const std::string& component) {
+void CrOSComponentInstaller::EmitInstalledSignal(const std::string& component) {
   if (delegate_)
     delegate_->EmitInstalledSignal(component);
 }
 
-bool CrOSComponentManager::IsRegistered(const std::string& name) {
+bool CrOSComponentInstaller::IsRegistered(const std::string& name) const {
   base::FilePath root;
   if (!base::PathService::Get(DIR_COMPONENT_USER, &root))
     return false;
@@ -282,19 +281,19 @@
   return base::PathExists(root.Append(kComponentsRootPath).Append(name));
 }
 
-void CrOSComponentManager::Register(ComponentUpdateService* cus,
-                                    const ComponentConfig& config,
-                                    base::OnceClosure register_callback) {
+void CrOSComponentInstaller::Register(ComponentUpdateService* cus,
+                                      const ComponentConfig& config,
+                                      base::OnceClosure register_callback) {
   auto installer = base::MakeRefCounted<ComponentInstaller>(
-      std::make_unique<CrOSComponentInstallerPolicy>(config));
+      std::make_unique<CrOSComponentInstallerPolicy>(config, this));
   installer->Register(cus, std::move(register_callback));
 }
 
-void CrOSComponentManager::Install(ComponentUpdateService* cus,
-                                   const std::string& name,
-                                   UpdatePolicy update_policy,
-                                   MountPolicy mount_policy,
-                                   LoadCallback load_callback) {
+void CrOSComponentInstaller::Install(ComponentUpdateService* cus,
+                                     const std::string& name,
+                                     UpdatePolicy update_policy,
+                                     MountPolicy mount_policy,
+                                     LoadCallback load_callback) {
   const ComponentConfig* config = FindConfig(name);
   if (!config) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -306,14 +305,14 @@
 
   Register(cus, *config,
            base::BindOnce(
-               &CrOSComponentManager::StartInstall, base::Unretained(this), cus,
-               name, GenerateId(config->sha2hash), update_policy,
-               base::BindOnce(&CrOSComponentManager::FinishInstall,
+               &CrOSComponentInstaller::StartInstall, base::Unretained(this),
+               cus, name, GenerateId(config->sha2hash), update_policy,
+               base::BindOnce(&CrOSComponentInstaller::FinishInstall,
                               base::Unretained(this), name, mount_policy,
                               std::move(load_callback))));
 }
 
-void CrOSComponentManager::StartInstall(
+void CrOSComponentInstaller::StartInstall(
     ComponentUpdateService* cus,
     const std::string& name,
     const std::string& id,
@@ -337,10 +336,10 @@
                                            std::move(install_callback));
 }
 
-void CrOSComponentManager::FinishInstall(const std::string& name,
-                                         MountPolicy mount_policy,
-                                         LoadCallback load_callback,
-                                         update_client::Error error) {
+void CrOSComponentInstaller::FinishInstall(const std::string& name,
+                                           MountPolicy mount_policy,
+                                           LoadCallback load_callback,
+                                           update_client::Error error) {
   if (error != update_client::Error::NONE) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -355,8 +354,8 @@
   }
 }
 
-void CrOSComponentManager::LoadInternal(const std::string& name,
-                                        LoadCallback load_callback) {
+void CrOSComponentInstaller::LoadInternal(const std::string& name,
+                                          LoadCallback load_callback) {
   const base::FilePath path = GetCompatiblePath(name);
   // path is empty if no compatible component is available to load.
   if (!path.empty()) {
@@ -364,7 +363,7 @@
         ->GetImageLoaderClient()
         ->LoadComponentAtPath(
             name, path,
-            base::BindOnce(&CrOSComponentManager::FinishLoad,
+            base::BindOnce(&CrOSComponentInstaller::FinishLoad,
                            base::Unretained(this), std::move(load_callback),
                            base::TimeTicks::Now(), name));
   } else {
@@ -376,10 +375,10 @@
   }
 }
 
-void CrOSComponentManager::FinishLoad(LoadCallback load_callback,
-                                      const base::TimeTicks start_time,
-                                      const std::string& name,
-                                      base::Optional<base::FilePath> result) {
+void CrOSComponentInstaller::FinishLoad(LoadCallback load_callback,
+                                        const base::TimeTicks start_time,
+                                        const std::string& name,
+                                        base::Optional<base::FilePath> result) {
   // Report component image mount time.
   UMA_HISTOGRAM_LONG_TIMES("ComponentUpdater.ChromeOS.MountTime",
                            base::TimeTicks::Now() - start_time);
@@ -396,7 +395,7 @@
   }
 }
 
-void CrOSComponentManager::RegisterN(
+void CrOSComponentInstaller::RegisterN(
     const std::vector<ComponentConfig>& configs) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   ComponentUpdateService* updater = g_browser_process->component_updater();
@@ -405,7 +404,7 @@
   }
 }
 
-bool CrOSComponentManager::IsCompatible(const std::string& name) const {
+bool CrOSComponentInstaller::IsCompatible(const std::string& name) const {
   return compatible_components_.count(name) > 0;
 }
 
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.h b/chrome/browser/component_updater/cros_component_installer_chromeos.h
index ece47580..01b95216 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.h
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.h
@@ -12,6 +12,7 @@
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
 #include "base/optional.h"
+#include "chrome/browser/component_updater/cros_component_manager.h"
 #include "components/component_updater/component_installer.h"
 #include "components/component_updater/component_updater_service.h"
 #include "components/update_client/update_client.h"
@@ -20,6 +21,7 @@
 
 class ComponentUpdateService;
 class MetadataTable;
+class CrOSComponentInstaller;
 
 struct ComponentConfig {
   const char* name;
@@ -29,7 +31,9 @@
 
 class CrOSComponentInstallerPolicy : public ComponentInstallerPolicy {
  public:
-  explicit CrOSComponentInstallerPolicy(const ComponentConfig& config);
+  CrOSComponentInstallerPolicy(
+      const ComponentConfig& config,
+      CrOSComponentInstaller* cros_component_installer);
   ~CrOSComponentInstallerPolicy() override;
 
  private:
@@ -60,6 +64,8 @@
   virtual bool IsCompatible(const std::string& env_version_str,
                             const std::string& min_env_version_str);
 
+  CrOSComponentInstaller* const cros_component_installer_;
+
   const std::string name_;
   const std::string env_version_;
   std::vector<uint8_t> sha2_hash_;
@@ -68,86 +74,31 @@
 };
 
 // This class contains functions used to register and install a component.
-class CrOSComponentManager {
+class CrOSComponentInstaller : public CrOSComponentManager {
  public:
-  // Error needs to be consistent with CrosComponentManagerError in
-  // src/tools/metrics/histograms/enums.xml.
-  enum class Error {
-    NONE = 0,
-    UNKNOWN_COMPONENT = 1,  // Component requested does not exist.
-    INSTALL_FAILURE = 2,    // update_client fails to install component.
-    MOUNT_FAILURE = 3,      // Component can not be mounted.
-    COMPATIBILITY_CHECK_FAILED = 4,  // Compatibility check failed.
-    ERROR_MAX
-  };
-  // LoadCallback will always return the load result in |error|. If used in
-  // conjunction with the |kMount| policy below, return the mounted FilePath in
-  // |path|, or an empty |path| otherwise.
-  using LoadCallback =
-      base::OnceCallback<void(Error error, const base::FilePath& path)>;
-  // Policy on mount operation.
-  enum class MountPolicy {
-    // Mount the component if installed.
-    kMount,
-    // Skip the mount operation.
-    kDontMount,
-  };
-  // Policy on update operation.
-  enum class UpdatePolicy {
-    // Force component update.
-    kForce,
-    // Do not update if a compatible component is installed.
-    kDontForce,
-    // Do not run updater, even if a compatible component is not installed at
-    // the moment.
-    kSkip
-  };
+  explicit CrOSComponentInstaller(
+      std::unique_ptr<MetadataTable> metadata_table);
+  ~CrOSComponentInstaller() override;
 
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-    // Broadcasts a D-Bus signal for a successful component installation.
-    virtual void EmitInstalledSignal(const std::string& component) = 0;
-  };
-
-  explicit CrOSComponentManager(std::unique_ptr<MetadataTable> metadata_table);
-  ~CrOSComponentManager();
-
-  void SetDelegate(Delegate* delegate);
-
-  // Installs a component and keeps it up-to-date.
-  // The |load_callback| is run on the calling thread.
+  // CrOSComponentManager:
+  void SetDelegate(Delegate* delegate) override;
   void Load(const std::string& name,
             MountPolicy mount_policy,
             UpdatePolicy update_policy,
-            LoadCallback load_callback);
-
-  // Stops updating and removes a component.
-  // Returns true if the component was successfully unloaded
-  // or false if it couldn't be unloaded or already wasn't loaded.
-  bool Unload(const std::string& name);
-
-  // Register all installed components.
-  void RegisterInstalled();
-
-  // Saves the name and install path of a compatible component.
+            LoadCallback load_callback) override;
+  bool Unload(const std::string& name) override;
   void RegisterCompatiblePath(const std::string& name,
-                              const base::FilePath& path);
+                              const base::FilePath& path) override;
+  void RegisterInstalled() override;
 
-  // Removes the name and install path entry of a component.
-  void UnregisterCompatiblePath(const std::string& name);
-
-  // Returns installed path of a compatible component given |name|. Returns an
-  // empty path if the component isn't compatible.
-  base::FilePath GetCompatiblePath(const std::string& name) const;
+  void UnregisterCompatiblePath(const std::string& name) override;
+  base::FilePath GetCompatiblePath(const std::string& name) const override;
+  bool IsRegistered(const std::string& name) const override;
 
   // Called when a component is installed/updated.
   // Broadcasts a D-Bus signal for a successful component installation.
   void EmitInstalledSignal(const std::string& component);
 
-  // Returns true if any previously registered version of a component exists,
-  // even if it is incompatible.
-  bool IsRegistered(const std::string& name);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, RegisterComponent);
@@ -206,12 +157,12 @@
   base::flat_map<std::string, base::FilePath> compatible_components_;
 
   // A weak pointer to a Delegate for emitting D-Bus signal.
-  Delegate* delegate_;
+  Delegate* delegate_ = nullptr;
 
   // Table storing metadata (installs, usage, etc.).
   std::unique_ptr<MetadataTable> metadata_table_;
 
-  DISALLOW_COPY_AND_ASSIGN(CrOSComponentManager);
+  DISALLOW_COPY_AND_ASSIGN(CrOSComponentInstaller);
 };
 
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
index eace0e9..1ad087e 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
@@ -47,14 +47,14 @@
 class MockCrOSComponentInstallerPolicy : public CrOSComponentInstallerPolicy {
  public:
   explicit MockCrOSComponentInstallerPolicy(const ComponentConfig& config)
-      : CrOSComponentInstallerPolicy(config) {}
+      : CrOSComponentInstallerPolicy(config, nullptr) {}
   MOCK_METHOD2(IsCompatible,
                bool(const std::string& env_version_str,
                     const std::string& min_env_version_str));
 };
 
 TEST_F(CrOSComponentInstallerTest, CompatibleCrOSComponent) {
-  component_updater::CrOSComponentManager cros_component_manager(nullptr);
+  component_updater::CrOSComponentInstaller cros_component_manager(nullptr);
 
   const std::string kComponent = "a";
   EXPECT_FALSE(cros_component_manager.IsCompatible(kComponent));
@@ -94,7 +94,7 @@
 
 TEST_F(CrOSComponentInstallerTest, IsCompatibleOrNot) {
   ComponentConfig config{"", "", ""};
-  CrOSComponentInstallerPolicy policy(config);
+  CrOSComponentInstallerPolicy policy(config, nullptr);
   EXPECT_TRUE(policy.IsCompatible("1.0", "1.0"));
   EXPECT_TRUE(policy.IsCompatible("1.1", "1.0"));
   EXPECT_FALSE(policy.IsCompatible("1.0", "1.1"));
@@ -111,7 +111,7 @@
       "star-cups-driver", "1.1",
       "6d24de30f671da5aee6d463d9e446cafe9ddac672800a9defe86877dcde6c466"};
   EXPECT_CALL(*cus, RegisterComponent(testing::_)).Times(1);
-  component_updater::CrOSComponentManager cros_component_manager(nullptr);
+  component_updater::CrOSComponentInstaller cros_component_manager(nullptr);
   cros_component_manager.Register(cus.get(), config, base::OnceClosure());
   RunUntilIdle();
 }
diff --git a/chrome/browser/component_updater/cros_component_manager.h b/chrome/browser/component_updater/cros_component_manager.h
new file mode 100644
index 0000000..36e0914
--- /dev/null
+++ b/chrome/browser/component_updater/cros_component_manager.h
@@ -0,0 +1,101 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_MANAGER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_MANAGER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace component_updater {
+
+// This class contains functions used to register and install a component.
+class CrOSComponentManager {
+ public:
+  // Error needs to be consistent with CrosComponentManagerError in
+  // src/tools/metrics/histograms/enums.xml.
+  enum class Error {
+    NONE = 0,
+    UNKNOWN_COMPONENT = 1,  // Component requested does not exist.
+    INSTALL_FAILURE = 2,    // update_client fails to install component.
+    MOUNT_FAILURE = 3,      // Component can not be mounted.
+    COMPATIBILITY_CHECK_FAILED = 4,  // Compatibility check failed.
+    ERROR_MAX
+  };
+
+  // Policy on mount operation.
+  enum class MountPolicy {
+    // Mount the component if installed.
+    kMount,
+    // Skip the mount operation.
+    kDontMount,
+  };
+
+  // Policy on update operation.
+  enum class UpdatePolicy {
+    // Force component update.
+    kForce,
+    // Do not update if a compatible component is installed.
+    kDontForce,
+    // Do not run updater, even if a compatible component is not installed at
+    // the moment.
+    kSkip
+  };
+
+  // LoadCallback will always return the load result in |error|. If used in
+  // conjunction with the |kMount| policy below, return the mounted FilePath in
+  // |path|, or an empty |path| otherwise.
+  using LoadCallback =
+      base::OnceCallback<void(Error error, const base::FilePath& path)>;
+
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+    // Broadcasts a D-Bus signal for a successful component installation.
+    virtual void EmitInstalledSignal(const std::string& component) = 0;
+  };
+
+  virtual ~CrOSComponentManager() = default;
+
+  virtual void SetDelegate(Delegate* delegate) = 0;
+
+  // Installs a component and keeps it up-to-date.
+  // The |load_callback| is run on the calling thread.
+  virtual void Load(const std::string& name,
+                    MountPolicy mount_policy,
+                    UpdatePolicy update_policy,
+                    LoadCallback load_callback) = 0;
+
+  // Stops updating and removes a component.
+  // Returns true if the component was successfully unloaded
+  // or false if it couldn't be unloaded or already wasn't loaded.
+  virtual bool Unload(const std::string& name) = 0;
+
+  // Saves the name and install path of a compatible component.
+  virtual void RegisterCompatiblePath(const std::string& name,
+                                      const base::FilePath& path) = 0;
+
+  // Removes the name and install path entry of a component.
+  virtual void UnregisterCompatiblePath(const std::string& name) = 0;
+
+  // Returns installed path of a compatible component given |name|. Returns an
+  // empty path if the component isn't compatible.
+  virtual base::FilePath GetCompatiblePath(const std::string& name) const = 0;
+
+  // Returns true if any previously registered version of a component exists,
+  // even if it is incompatible.
+  virtual bool IsRegistered(const std::string& name) const = 0;
+
+  // Register all installed components.
+  virtual void RegisterInstalled() = 0;
+};
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_MANAGER_H_
diff --git a/chrome/browser/component_updater/fake_cros_component_manager.cc b/chrome/browser/component_updater/fake_cros_component_manager.cc
new file mode 100644
index 0000000..20d71595
--- /dev/null
+++ b/chrome/browser/component_updater/fake_cros_component_manager.cc
@@ -0,0 +1,216 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/component_updater/fake_cros_component_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace component_updater {
+
+FakeCrOSComponentManager::ComponentInfo::ComponentInfo(
+    Error load_response,
+    const base::FilePath& install_path,
+    const base::FilePath& mount_path)
+    : load_response(load_response),
+      install_path(install_path),
+      mount_path(mount_path) {
+  // If component load fails, neither install nor mount path should be set.
+  DCHECK(load_response == Error::NONE ||
+         (install_path.empty() && mount_path.empty()));
+  // Component should have install path set if it's expected to be loaded.
+  DCHECK(load_response != Error::NONE || !install_path.empty());
+}
+
+FakeCrOSComponentManager::ComponentInfo::~ComponentInfo() = default;
+
+FakeCrOSComponentManager::FakeCrOSComponentManager() = default;
+
+FakeCrOSComponentManager::~FakeCrOSComponentManager() = default;
+
+bool FakeCrOSComponentManager::FinishLoadRequest(
+    const std::string& name,
+    const ComponentInfo& component_info) {
+  if (!pending_loads_.count(name) || pending_loads_[name].empty()) {
+    LOG(ERROR) << "No pending load for " << name;
+    return false;
+  }
+
+  auto& pending_load = pending_loads_[name].front();
+  FinishComponentLoad(name, pending_load.mount_requested, component_info);
+
+  LoadCallback callback = std::move(pending_load.callback);
+  pending_loads_[name].pop_front();
+
+  std::move(callback).Run(component_info.load_response,
+                          component_info.load_response == Error::NONE
+                              ? component_info.mount_path
+                              : base::FilePath());
+  return true;
+}
+
+bool FakeCrOSComponentManager::ResetComponentState(const std::string& name,
+                                                   const ComponentInfo& state) {
+  if (!supported_components_.count(name))
+    return false;
+
+  installed_components_.erase(name);
+  mounted_components_.erase(name);
+
+  component_infos_.erase(name);
+  component_infos_.emplace(
+      name,
+      ComponentInfo(state.load_response, state.install_path, state.mount_path));
+  return true;
+}
+
+bool FakeCrOSComponentManager::HasPendingInstall(
+    const std::string& name) const {
+  DCHECK(queue_load_requests_);
+
+  const auto& it = pending_loads_.find(name);
+  return it != pending_loads_.end() && !it->second.empty();
+}
+
+bool FakeCrOSComponentManager::UpdateRequested(const std::string& name) const {
+  DCHECK(queue_load_requests_);
+
+  const auto& it = pending_loads_.find(name);
+  return it != pending_loads_.end() && !it->second.empty() &&
+         it->second.front().needs_update;
+}
+
+void FakeCrOSComponentManager::SetDelegate(Delegate* delegate) {
+  // No-op, not used by the fake.
+}
+
+void FakeCrOSComponentManager::Load(const std::string& name,
+                                    MountPolicy mount_policy,
+                                    UpdatePolicy update_policy,
+                                    LoadCallback load_callback) {
+  if (!supported_components_.count(name)) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(load_callback),
+                                  Error::UNKNOWN_COMPONENT, base::FilePath()));
+    return;
+  }
+
+  bool needs_update = update_policy == UpdatePolicy::kForce ||
+                      (!installed_components_.count(name) &&
+                       update_policy != UpdatePolicy::kSkip);
+
+  // The request has to be handled if the component is not yet installed, or it
+  // requires immediate update.
+  if (needs_update || !installed_components_.count(name)) {
+    HandlePendingRequest(name, mount_policy == MountPolicy::kMount,
+                         needs_update, std::move(load_callback));
+    return;
+  }
+
+  // Handle request if the component has yet to be mounted - e.g. if previous
+  // loads installed the component without mounting it.
+  if (!mounted_components_.count(name) && mount_policy == MountPolicy::kMount) {
+    HandlePendingRequest(name, true /*mount_requested*/, false /*needs_update*/,
+                         std::move(load_callback));
+    return;
+  }
+
+  // The component has been prevoiusly installed, and mounted as required by
+  // this load request - run the callback according to the existing state.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(load_callback), Error::NONE,
+                                mount_policy == MountPolicy::kMount
+                                    ? mounted_components_[name]
+                                    : base::FilePath()));
+}
+
+bool FakeCrOSComponentManager::Unload(const std::string& name) {
+  registered_components_.erase(name);
+  mounted_components_.erase(name);
+  installed_components_.erase(name);
+  return true;
+}
+
+void FakeCrOSComponentManager::RegisterCompatiblePath(
+    const std::string& name,
+    const base::FilePath& path) {
+  installed_components_[name] = path;
+}
+
+void FakeCrOSComponentManager::UnregisterCompatiblePath(
+    const std::string& name) {
+  installed_components_.erase(name);
+}
+
+base::FilePath FakeCrOSComponentManager::GetCompatiblePath(
+    const std::string& name) const {
+  const auto& it = installed_components_.find(name);
+  if (it == installed_components_.end())
+    return base::FilePath();
+  return it->second;
+}
+
+bool FakeCrOSComponentManager::IsRegistered(const std::string& name) const {
+  return registered_components_.count(name);
+}
+
+void FakeCrOSComponentManager::RegisterInstalled() {
+  NOTIMPLEMENTED();
+}
+
+FakeCrOSComponentManager::LoadRequest::LoadRequest(bool mount_requested,
+                                                   bool needs_update,
+                                                   LoadCallback callback)
+    : mount_requested(mount_requested),
+      needs_update(needs_update),
+      callback(std::move(callback)) {}
+
+FakeCrOSComponentManager::LoadRequest::~LoadRequest() = default;
+
+void FakeCrOSComponentManager::HandlePendingRequest(const std::string& name,
+                                                    bool mount_requested,
+                                                    bool needs_update,
+                                                    LoadCallback callback) {
+  if (queue_load_requests_) {
+    pending_loads_[name].emplace_back(mount_requested, needs_update,
+                                      std::move(callback));
+    return;
+  }
+
+  const auto& component_info = component_infos_.find(name);
+  if (component_info == component_infos_.end()) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), Error::INSTALL_FAILURE,
+                                  base::FilePath()));
+    return;
+  }
+
+  FinishComponentLoad(name, mount_requested, component_info->second);
+
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), component_info->second.load_response,
+                     component_info->second.mount_path));
+}
+
+void FakeCrOSComponentManager::FinishComponentLoad(
+    const std::string& name,
+    bool mount_requested,
+    const ComponentInfo& component_info) {
+  registered_components_.insert(name);
+  if (component_info.load_response != Error::NONE)
+    return;
+
+  DCHECK_EQ(mount_requested, !component_info.mount_path.empty());
+  installed_components_[name] = component_info.install_path;
+  if (mount_requested)
+    mounted_components_[name] = component_info.mount_path;
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/fake_cros_component_manager.h b/chrome/browser/component_updater/fake_cros_component_manager.h
new file mode 100644
index 0000000..2be89ab6
--- /dev/null
+++ b/chrome/browser/component_updater/fake_cros_component_manager.h
@@ -0,0 +1,159 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_FAKE_CROS_COMPONENT_MANAGER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_FAKE_CROS_COMPONENT_MANAGER_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "chrome/browser/component_updater/cros_component_manager.h"
+
+namespace component_updater {
+
+// This fake implementation of cros component manager. Intended to be used in
+// tests to abstract away the cros component manager dependency on imageloader
+// and component updater services, and local file system.
+class FakeCrOSComponentManager : public CrOSComponentManager {
+ public:
+  // Information about how fake component manager should "load" a component.
+  struct ComponentInfo {
+    ComponentInfo(Error load_response,
+                  const base::FilePath& install_path,
+                  const base::FilePath& mount_path);
+    ~ComponentInfo();
+
+    // The status load requests for the component should produce.
+    Error load_response;
+
+    // The local path where the fake component manager thinks the component is
+    // installed.
+    base::FilePath install_path;
+
+    // The path where the fake component manager thinks the component is
+    // mounted.
+    base::FilePath mount_path;
+  };
+
+  FakeCrOSComponentManager();
+  ~FakeCrOSComponentManager() override;
+
+  void set_queue_load_requests(bool queue_load_requests) {
+    queue_load_requests_ = queue_load_requests;
+  }
+  void set_supported_components(const std::set<std::string>& components) {
+    supported_components_ = components;
+  }
+  void set_registered_components(const std::set<std::string>& components) {
+    registered_components_ = components;
+  }
+
+  // Finishes a queued component load request. Should be used only if
+  // |queue_load_requests_| is set.
+  bool FinishLoadRequest(const std::string& name, const ComponentInfo& state);
+
+  // If the component is "loaded", clears the recorded install and mount paths,
+  // and sets the info about how future load requests for the component should
+  // be handled.
+  bool ResetComponentState(const std::string& name, const ComponentInfo& state);
+
+  // Whether any component loads are pending. Expected to be used only if
+  // |queue_load_requests_| is set.
+  bool HasPendingInstall(const std::string& name) const;
+
+  // Whether the next pending component load requests triggers immediate
+  // component update request. Expected to be used only if
+  // |queue_load_requests_| is set.
+  bool UpdateRequested(const std::string& name) const;
+
+  // CrOSComponentManager:
+  void SetDelegate(Delegate* delegate) override;
+  void Load(const std::string& name,
+            MountPolicy mount_policy,
+            UpdatePolicy update_policy,
+            LoadCallback load_callback) override;
+  bool Unload(const std::string& name) override;
+  void RegisterCompatiblePath(const std::string& name,
+                              const base::FilePath& path) override;
+  void UnregisterCompatiblePath(const std::string& name) override;
+  base::FilePath GetCompatiblePath(const std::string& name) const override;
+  bool IsRegistered(const std::string& name) const override;
+  void RegisterInstalled() override;
+
+ private:
+  // Describes pending component load request.
+  struct LoadRequest {
+    LoadRequest(bool mount_requested, bool needs_update, LoadCallback callback);
+    ~LoadRequest();
+
+    // Whether the component should be mounted as part of the load request.
+    bool mount_requested;
+
+    // Whether the request should start immediate component update check.
+    bool needs_update;
+
+    // The load request callback.
+    LoadCallback callback;
+  };
+
+  // Handles a load request for a component, either by queueing it (if
+  // queue_load_requests_ is set), or setting the new component state depending
+  // on component_infos_.
+  // |name|: the component name.
+  // |mount_requested|: whether the component mount was requested as part of the
+  //     load request.
+  // |needs_update|: whether the load request triggers immediate update attempt.
+  // |callback|: to be called when the load request finishes.
+  void HandlePendingRequest(const std::string& name,
+                            bool mount_requested,
+                            bool needs_update,
+                            LoadCallback callback);
+  // Updates the fake component loader state on a successful component load
+  // request.
+  // |name|: the component name.
+  // |mount_requested|: whether the component should be mounted.
+  // |component_info|: the component's load information.
+  void FinishComponentLoad(const std::string& name,
+                           bool mount_requested,
+                           const ComponentInfo& component_info);
+
+  // Whether the load requests should be queued up, and not handled immediately.
+  // When this is set, component load requests should be completed using
+  // FinishLoadRequest().
+  bool queue_load_requests_ = false;
+
+  // Set of components that can be handled by this component manager.
+  std::set<std::string> supported_components_;
+
+  // Set of components registered with this component manager - used primarily
+  // by IsRegistered() implementation.
+  std::set<std::string> registered_components_;
+
+  // The component information registered using ResetComponentInfo() - used to
+  // handle component load requests when queue_load_requests_ is not set.
+  std::map<std::string, ComponentInfo> component_infos_;
+
+  // List of pending component load requests per component. Used only if
+  // queue_load_requests_ is set.
+  std::map<std::string, std::list<LoadRequest>> pending_loads_;
+
+  // Maps the currently installed (and loaded) components to their installation
+  // path.
+  std::map<std::string, base::FilePath> installed_components_;
+
+  // Maps the currently mounted components to their mount point path.
+  std::map<std::string, base::FilePath> mounted_components_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCrOSComponentManager);
+};
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_FAKE_CROS_COMPONENT_MANAGER_H_
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc
index 0a600743..4d06926 100644
--- a/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/extensions/api/identity/identity_constants.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
@@ -42,6 +43,11 @@
 
 namespace extensions {
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+const base::Feature kExtensionsAllAccountsFeature{
+    "ExtensionsAllAccounts", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 IdentityTokenCacheValue::IdentityTokenCacheValue()
     : status_(CACHE_STATUS_NOTFOUND) {}
 
@@ -153,6 +159,19 @@
   return g_identity_api_factory.Pointer();
 }
 
+bool IdentityAPI::AreExtensionsRestrictedToPrimaryAccount() {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  if (!signin::DiceMethodGreaterOrEqual(
+          AccountConsistencyModeManager::GetMethodForProfile(profile_),
+          signin::AccountConsistencyMethod::kDiceMigration)) {
+    return true;
+  }
+  return !base::FeatureList::IsEnabled(kExtensionsAllAccountsFeature);
+#else
+  return true;
+#endif
+}
+
 void IdentityAPI::OnRefreshTokenAvailable(const std::string& account_id) {
   const AccountInfo& account_info =
       AccountTrackerServiceFactory::GetForProfile(profile_)->GetAccountInfo(
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h
index 5fc41ec..964c7b01 100644
--- a/chrome/browser/extensions/api/identity/identity_api.h
+++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -12,11 +12,13 @@
 #include <vector>
 
 #include "base/callback_list.h"
+#include "base/feature_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/extensions/api/identity/extension_token_key.h"
 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h"
 #include "chrome/browser/extensions/api/identity/identity_get_accounts_function.h"
@@ -28,6 +30,7 @@
 #include "chrome/browser/extensions/api/identity/web_auth_flow.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/signin_buildflags.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/event_router.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
@@ -41,6 +44,11 @@
 
 namespace extensions {
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Enables all accounts in extensions.
+extern const base::Feature kExtensionsAllAccountsFeature;
+#endif
+
 class IdentityTokenCacheValue {
  public:
   IdentityTokenCacheValue();
@@ -112,6 +120,10 @@
     on_signin_changed_callback_for_testing_ = callback;
   }
 
+  // Whether the chrome.identity API should use all accounts or the primary
+  // account only.
+  bool AreExtensionsRestrictedToPrimaryAccount();
+
  private:
   friend class BrowserContextKeyedAPIFactory<IdentityAPI>;
 
diff --git a/chrome/browser/extensions/api/identity/identity_api_unittest.cc b/chrome/browser/extensions/api/identity/identity_api_unittest.cc
new file mode 100644
index 0000000..ffb0cc7
--- /dev/null
+++ b/chrome/browser/extensions/api/identity/identity_api_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/identity/identity_api.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/signin/scoped_account_consistency.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/signin/core/browser/signin_buildflags.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Tests that all accounts in extensions is only enabled when Dice is enabled.
+TEST(IdentityApiTest, DiceAllAccountsExtensions) {
+  content::TestBrowserThreadBundle test_thread_bundle;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kExtensionsAllAccountsFeature);
+
+  {
+    ScopedAccountConsistencyDice scoped_dice;
+    TestingProfile profile;
+    IdentityAPI api(&profile);
+    EXPECT_FALSE(api.AreExtensionsRestrictedToPrimaryAccount());
+    api.Shutdown();
+  }
+
+  {
+    ScopedAccountConsistencyDiceFixAuthErrors scoped_dice_fix_errors;
+    TestingProfile profile;
+    IdentityAPI api(&profile);
+    EXPECT_TRUE(api.AreExtensionsRestrictedToPrimaryAccount());
+    api.Shutdown();
+  }
+}
+#endif
+
+TEST(IdentityApiTest, AllAccountsExtensionDisabled) {
+  content::TestBrowserThreadBundle test_thread_bundle;
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(kExtensionsAllAccountsFeature);
+#endif
+  TestingProfile profile;
+  IdentityAPI api(&profile);
+  EXPECT_TRUE(api.AreExtensionsRestrictedToPrimaryAccount());
+  api.Shutdown();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 6420425..16dc019 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -13,8 +13,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/identity/identity_api.h"
 #include "chrome/browser/extensions/api/identity/identity_constants.h"
@@ -50,10 +52,9 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
-#include "components/signin/core/browser/profile_management_switches.h"
+#include "components/signin/core/browser/signin_buildflags.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
-#include "components/signin/core/browser/signin_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/test/test_utils.h"
@@ -506,6 +507,10 @@
     return account_tracker->SeedAccountInfo(gaia, email);
   }
 
+  IdentityAPI* id_api() {
+    return IdentityAPI::GetFactoryInstance()->Get(browser()->profile());
+  }
+
   FakeSigninManagerForTesting* signin_manager_;
   FakeProfileOAuth2TokenService* token_service_;
 
@@ -515,9 +520,11 @@
 };
 
 class IdentityGetAccountsFunctionTest : public IdentityTestWithSignin {
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kExtensionsMultiAccount);
+ public:
+  IdentityGetAccountsFunctionTest() {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+    feature_list_.InitAndEnableFeature(kExtensionsAllAccountsFeature);
+#endif
   }
 
  protected:
@@ -586,10 +593,17 @@
 
     return testing::AssertionFailure(msg);
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, MultiAccountOn) {
-  EXPECT_TRUE(signin::IsExtensionsMultiAccount());
+IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, AllAccountsOn) {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  EXPECT_FALSE(id_api()->AreExtensionsRestrictedToPrimaryAccount());
+#else
+  EXPECT_TRUE(id_api()->AreExtensionsRestrictedToPrimaryAccount());
+#endif
 }
 
 IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoneSignedIn) {
@@ -617,21 +631,31 @@
 IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, TwoAccountsSignedIn) {
   SignIn("primary@example.com");
   AddAccount("secondary@example.com");
-  EXPECT_TRUE(ExpectGetAccounts({"gaia_id_for_primary@example.com",
-                                 "gaia_id_for_secondary@example.com"}));
+  if (!id_api()->AreExtensionsRestrictedToPrimaryAccount()) {
+    EXPECT_TRUE(ExpectGetAccounts({"gaia_id_for_primary@example.com",
+                                   "gaia_id_for_secondary@example.com"}));
+  } else {
+    EXPECT_TRUE(ExpectGetAccounts({"gaia_id_for_primary@example.com"}));
+  }
 }
 
 class IdentityOldProfilesGetAccountsFunctionTest
     : public IdentityGetAccountsFunctionTest {
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
-    // Don't add the multi-account switch that parent class would have.
+ public:
+  IdentityOldProfilesGetAccountsFunctionTest() {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+    // Disable the feature that was enabled by the parent class.
+    feature_list_.InitAndDisableFeature(kExtensionsAllAccountsFeature);
+#endif
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest,
-                       MultiAccountOff) {
-  EXPECT_FALSE(signin::IsExtensionsMultiAccount());
+                       AllAccountsOff) {
+  EXPECT_TRUE(id_api()->AreExtensionsRestrictedToPrimaryAccount());
 }
 
 IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest,
@@ -705,9 +729,14 @@
     : public IdentityTestWithSignin,
       public OAuth2TokenService::DiagnosticsObserver {
  public:
+  GetAuthTokenFunctionTest() {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+    feature_list_.InitAndEnableFeature(kExtensionsAllAccountsFeature);
+#endif
+  }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     IdentityTestWithSignin::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kExtensionsMultiAccount);
   }
 
   std::string IssueLoginAccessTokenForAccount(const std::string& account_id) {
@@ -765,10 +794,6 @@
     return ext;
   }
 
-  IdentityAPI* id_api() {
-    return IdentityAPI::GetFactoryInstance()->Get(browser()->profile());
-  }
-
   const std::string& GetPrimaryAccountId() {
     SigninManagerBase* signin_manager =
         SigninManagerFactory::GetForProfile(browser()->profile());
@@ -812,6 +837,7 @@
     std::move(on_access_token_requested_).Run();
   }
 
+  base::test::ScopedFeatureList feature_list_;
   std::string extension_id_;
   std::set<std::string> oauth_scopes_;
 };
@@ -1777,6 +1803,10 @@
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        MultiSecondaryUserManuallyIssueToken) {
+  // This test is only relevant if extensions see all accounts.
+  if (id_api()->AreExtensionsRestrictedToPrimaryAccount())
+    return;
+
   std::string primary_account_id = SignIn("primary@example.com");
   std::string secondary_account_id = AddAccount("secondary@example.com");
 
@@ -1823,6 +1853,10 @@
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        MultiSecondaryNonInteractiveMintFailure) {
+  // This test is only relevant if extensions see all accounts.
+  if (id_api()->AreExtensionsRestrictedToPrimaryAccount())
+    return;
+
   SignIn("primary@example.com");
   AddAccount("secondary@example.com");
 
@@ -1841,6 +1875,10 @@
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        MultiSecondaryNonInteractiveLoginAccessTokenFailure) {
+  // This test is only relevant if extensions see all accounts.
+  if (id_api()->AreExtensionsRestrictedToPrimaryAccount())
+    return;
+
   SignIn("primary@example.com");
   AddAccount("secondary@example.com");
 
@@ -1857,6 +1895,10 @@
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        MultiSecondaryInteractiveApprovalAborted) {
+  // This test is only relevant if extensions see all accounts.
+  if (id_api()->AreExtensionsRestrictedToPrimaryAccount())
+    return;
+
   SignIn("primary@example.com");
   AddAccount("secondary@example.com");
 
diff --git a/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc b/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
index bc4e0341..ed995e4 100644
--- a/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
@@ -6,8 +6,8 @@
 
 #include "chrome/browser/extensions/api/identity/identity_api.h"
 #include "chrome/browser/extensions/api/identity/identity_constants.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/identity.h"
-#include "components/signin/core/browser/profile_management_switches.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/identity/public/mojom/account.mojom.h"
@@ -41,10 +41,20 @@
     std::vector<identity::mojom::AccountPtr> accounts) {
   std::unique_ptr<base::ListValue> infos(new base::ListValue());
 
-  // If there is no primary account or the primary account has no refresh token
-  // available, short-circuit out.
-  if (accounts.empty() || !accounts[0]->state.is_primary_account ||
-      !accounts[0]->state.has_refresh_token) {
+  if (accounts.empty()) {
+    Respond(OneArgument(std::move(infos)));
+    return;
+  }
+
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  bool primary_account_only = IdentityAPI::GetFactoryInstance()
+                                  ->Get(profile)
+                                  ->AreExtensionsRestrictedToPrimaryAccount();
+
+  // If extensions are restricted to the primary account and there is no valid
+  // primary account, short-circuit out.
+  if (primary_account_only && (!accounts[0]->state.is_primary_account ||
+                               !accounts[0]->state.has_refresh_token)) {
     Respond(OneArgument(std::move(infos)));
     return;
   }
@@ -54,8 +64,9 @@
     account_info.id = account->info.gaia;
     infos->Append(account_info.ToValue());
 
-    // Stop after the primary account if extensions are not multi-account.
-    if (!signin::IsExtensionsMultiAccount())
+    // Stop after the primary account if extensions are restricted to the
+    // primary account.
+    if (primary_account_only)
       break;
   }
 
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 41b2cc1..bd28b06 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -23,7 +23,6 @@
 #include "chrome/common/extensions/api/identity.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/profile_management_switches.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "content/public/common/service_manager_connection.h"
@@ -151,7 +150,10 @@
   // Detect and handle the case where the extension is using an account other
   // than the primary account.
   if (!extension_gaia_id.empty() && extension_gaia_id != primary_gaia_id) {
-    if (!signin::IsExtensionsMultiAccount()) {
+    bool primary_account_only = IdentityAPI::GetFactoryInstance()
+                                    ->Get(GetProfile())
+                                    ->AreExtensionsRestrictedToPrimaryAccount();
+    if (primary_account_only) {
       // TODO(courage): should this be a different error?
       CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
       return;
diff --git a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
index f967a3e..38be02f 100644
--- a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
+++ b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
@@ -27,27 +27,16 @@
  public:
   InstanceIDApiTest();
 
- protected:
-  void SetUp() override;
-  void TearDown() override;
-
  private:
+  gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
+      scoped_testing_factory_installer_;
+
   DISALLOW_COPY_AND_ASSIGN(InstanceIDApiTest);
 };
 
-InstanceIDApiTest::InstanceIDApiTest() {
-}
-
-void InstanceIDApiTest::SetUp() {
-  gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-      base::BindRepeating(&gcm::FakeGCMProfileService::Build));
-  ExtensionApiTest::SetUp();
-}
-
-void InstanceIDApiTest::TearDown() {
-  gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-      BrowserContextKeyedServiceFactory::TestingFactory());
-}
+InstanceIDApiTest::InstanceIDApiTest()
+    : scoped_testing_factory_installer_(
+          base::BindRepeating(&gcm::FakeGCMProfileService::Build)) {}
 
 IN_PROC_BROWSER_TEST_F(InstanceIDApiTest, GetID) {
   ASSERT_TRUE(RunExtensionTest("instance_id/get_id"));
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index ebea6bc2..8bbdaa2 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -576,12 +576,6 @@
 // web pages.
 IN_PROC_BROWSER_TEST_F(CommandsApiTest,
                        OverwriteBookmarkShortcutByUserOverridesWebKeybinding) {
-#if defined(OS_MACOSX)
-  // This doesn't work in MacViews mode: https://crbug.com/845503 is the likely
-  // root cause.
-  if (!views_mode_controller::IsViewsBrowserCocoa())
-    return;
-#endif
   ASSERT_TRUE(embedded_test_server()->Start());
 
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index 5d10948..46f476a6 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -474,7 +474,11 @@
 class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
  public:
   ServiceWorkerPushMessagingTest()
-      : gcm_driver_(nullptr), push_service_(nullptr) {}
+      : scoped_testing_factory_installer_(
+            base::BindRepeating(&gcm::FakeGCMProfileService::Build)),
+        gcm_driver_(nullptr),
+        push_service_(nullptr) {}
+
   ~ServiceWorkerPushMessagingTest() override {}
 
   void GrantNotificationPermissionForTest(const GURL& url) {
@@ -500,12 +504,6 @@
     ServiceWorkerTest::SetUpCommandLine(command_line);
   }
 
-  void SetUp() override {
-    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-        base::BindRepeating(&gcm::FakeGCMProfileService::Build));
-    ServiceWorkerTest::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     NotificationDisplayServiceFactory::GetInstance()->SetTestingFactory(
         profile(),
@@ -521,18 +519,15 @@
     ServiceWorkerTest::SetUpOnMainThread();
   }
 
-  void TearDown() override {
-    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-        BrowserContextKeyedServiceFactory::TestingFactory());
-    ServiceWorkerTest::TearDown();
-  }
-
   instance_id::FakeGCMDriverForInstanceID* gcm_driver() const {
     return gcm_driver_;
   }
   PushMessagingServiceImpl* push_service() const { return push_service_; }
 
  private:
+  gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
+      scoped_testing_factory_installer_;
+
   instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
   PushMessagingServiceImpl* push_service_;
 
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index e4e6fad3..891553cf 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -681,8 +681,14 @@
 void FileSelectHelper::RenderFrameHostChanged(
     content::RenderFrameHost* old_host,
     content::RenderFrameHost* new_host) {
-  if (old_host == render_frame_host_)
+  if (!render_frame_host_)
+    return;
+  // The |old_host| and its children are now pending deletion. Do not give them
+  // file access past this point.
+  if (render_frame_host_ == old_host ||
+      render_frame_host_->IsDescendantOf(old_host)) {
     render_frame_host_ = nullptr;
+  }
 }
 
 void FileSelectHelper::RenderFrameDeleted(
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index cca9bfc..59c27e4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -217,6 +217,11 @@
     "print server, instead of the cloud print interface in the Print "
     "Preview WebUI.";
 
+const char kFCMInvalidationsName[] =
+    "Enable invalidations delivery via new FCM based protocol";
+const char kFCMInvalidationsDescription[] =
+    "Use the new FCM-based protocol for deliveling invalidations";
+
 const char kForceColorProfileSRGB[] = "sRGB";
 const char kForceColorProfileP3[] = "Display P3 D65";
 const char kForceColorProfileColorSpin[] = "Color spin with gamma 2.4";
@@ -2231,8 +2236,8 @@
 
 const char kCCTModuleCacheName[] = "Chrome Custom Tabs Module Cache";
 const char kCCTModuleCacheDescription[] =
-    "Enables a cache for dynamically loaded modules in Chrome Custom Tabs, "
-    "on Android.";
+    "Enables a cache for dynamically loaded modules in Chrome Custom Tabs. "
+    "Under mild memory pressure the cache may be retained for some time";
 
 const char kChromeDuetName[] = "Chrome Duet";
 const char kChromeDuetDescription[] =
@@ -2975,11 +2980,6 @@
 const char kMacTouchBarName[] = "Hardware Touch Bar";
 const char kMacTouchBarDescription[] = "Control the use of the Touch Bar.";
 
-const char kMacV2SandboxName[] = "Mac V2 Sandbox";
-const char kMacV2SandboxDescription[] =
-    "Eliminates the unsandboxed warmup phase and sandboxes processes for their "
-    "entire life cycle.";
-
 const char kMacViewsNativeAppWindowsName[] = "Toolkit-Views App Windows.";
 const char kMacViewsNativeAppWindowsDescription[] =
     "Controls whether to use Toolkit-Views based Chrome App windows.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8d9eca5f..acd6e485 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -155,6 +155,9 @@
 extern const char kCloudPrinterHandlerName[];
 extern const char kCloudPrinterHandlerDescription[];
 
+extern const char kFCMInvalidationsName[];
+extern const char kFCMInvalidationsDescription[];
+
 extern const char kForceColorProfileSRGB[];
 extern const char kForceColorProfileP3[];
 extern const char kForceColorProfileColorSpin[];
@@ -1797,9 +1800,6 @@
 extern const char kMacTouchBarName[];
 extern const char kMacTouchBarDescription[];
 
-extern const char kMacV2SandboxName[];
-extern const char kMacV2SandboxDescription[];
-
 extern const char kMacViewsNativeAppWindowsName[];
 extern const char kMacViewsNativeAppWindowsDescription[];
 
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.cc b/chrome/browser/gcm/gcm_profile_service_factory.cc
index 2537432..28c87d5b 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -76,6 +76,17 @@
 
 }  // namespace
 
+GCMProfileServiceFactory::ScopedTestingFactoryInstaller::
+    ScopedTestingFactoryInstaller(TestingFactory testing_factory) {
+  DCHECK(!GetTestingFactory());
+  GetTestingFactory() = std::move(testing_factory);
+}
+
+GCMProfileServiceFactory::ScopedTestingFactoryInstaller::
+    ~ScopedTestingFactoryInstaller() {
+  GetTestingFactory() = BrowserContextKeyedServiceFactory::TestingFactory();
+}
+
 // static
 GCMProfileService* GCMProfileServiceFactory::GetForProfile(
     content::BrowserContext* profile) {
@@ -92,12 +103,6 @@
   return base::Singleton<GCMProfileServiceFactory>::get();
 }
 
-// static
-void GCMProfileServiceFactory::SetGlobalTestingFactory(
-    BrowserContextKeyedServiceFactory::TestingFactory factory) {
-  GetTestingFactory() = std::move(factory);
-}
-
 GCMProfileServiceFactory::GCMProfileServiceFactory()
     : BrowserContextKeyedServiceFactory(
         "GCMProfileService",
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.h b/chrome/browser/gcm/gcm_profile_service_factory.h
index 997f7d865..8786f9f 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.h
+++ b/chrome/browser/gcm/gcm_profile_service_factory.h
@@ -21,7 +21,18 @@
  public:
   static GCMProfileService* GetForProfile(content::BrowserContext* profile);
   static GCMProfileServiceFactory* GetInstance();
-  static void SetGlobalTestingFactory(TestingFactory factory);
+
+  // Helper registering a testing factory. Needs to be instantiated before the
+  // factory is accessed in your test, and deallocated after the last access.
+  // Usually this is achieved by putting this object as the first member in
+  // your test fixture.
+  class ScopedTestingFactoryInstaller {
+   public:
+    explicit ScopedTestingFactoryInstaller(TestingFactory testing_factory);
+    ~ScopedTestingFactoryInstaller();
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedTestingFactoryInstaller);
+  };
 
  private:
   friend struct base::DefaultSingletonTraits<GCMProfileServiceFactory>;
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
index 8d258cf..a9d311b 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
@@ -36,6 +36,13 @@
       break;                                                               \
   }
 
+#define RESOURCE_BYTES_HISTOGRAM(suffix, was_cached, value)                \
+  if (was_cached) {                                                        \
+    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Cache." suffix, value);   \
+  } else {                                                                 \
+    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Network." suffix, value); \
+  }
+
 // Finds the RenderFrameHost for the handle, possibly using the FrameTreeNode
 // ID directly if the the handle has not been committed.
 // NOTE: Unsafe with respect to security privileges.
@@ -434,42 +441,48 @@
 
 void AdsPageLoadMetricsObserver::RecordResourceMimeHistograms(
     const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  int64_t data_length = resource->was_fetched_via_cache
+                            ? resource->encoded_body_length
+                            : resource->received_data_length;
   ResourceMimeType mime_type = GetResourceMimeType(resource);
   if (mime_type == ResourceMimeType::kImage) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.Image",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.Image", resource->was_fetched_via_cache,
+                             data_length);
   } else if (mime_type == ResourceMimeType::kJavascript) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.JS",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.JS", resource->was_fetched_via_cache,
+                             data_length);
   } else if (mime_type == ResourceMimeType::kVideo) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.Video",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.Video", resource->was_fetched_via_cache,
+                             data_length);
   } else if (mime_type == ResourceMimeType::kCss) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.CSS",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.CSS", resource->was_fetched_via_cache,
+                             data_length);
   } else if (mime_type == ResourceMimeType::kHtml) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.HTML",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.HTML", resource->was_fetched_via_cache,
+                             data_length);
   } else if (mime_type == ResourceMimeType::kOther) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mime.Other",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mime.Other", resource->was_fetched_via_cache,
+                             data_length);
   }
 }
 
 void AdsPageLoadMetricsObserver::RecordResourceHistograms(
     const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  int64_t data_length = resource->was_fetched_via_cache
+                            ? resource->encoded_body_length
+                            : resource->received_data_length;
   if (resource->is_main_frame_resource && resource->reported_as_ad_resource) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mainframe.AdResource",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mainframe.AdResource",
+                             resource->was_fetched_via_cache, data_length);
   } else if (resource->is_main_frame_resource) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Mainframe.VanillaResource",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Mainframe.VanillaResource",
+                             resource->was_fetched_via_cache, data_length);
   } else if (resource->reported_as_ad_resource) {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Subframe.AdResource",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Subframe.AdResource",
+                             resource->was_fetched_via_cache, data_length);
   } else {
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Subframe.VanillaResource",
-                         resource->received_data_length);
+    RESOURCE_BYTES_HISTOGRAM("Subframe.VanillaResource",
+                             resource->was_fetched_via_cache, data_length);
   }
 
   // Only report sizes by mime type for ad resources.
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 073a39b..a905b96 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -344,14 +344,14 @@
 
   // Verify correct numbers of resources are recorded.
   histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Mainframe.VanillaResource", 1);
+      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 1);
   histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Mainframe.AdResource", 1);
+      "Ads.ResourceUsage.Size.Network.Mainframe.AdResource", 1);
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 1);
   // Verify unfinished resource not yet recorded.
   histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Subframe.AdResource", 1);
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Subframe.VanillaResource", 0);
+      "Ads.ResourceUsage.Size.Network.Subframe.VanillaResource", 0);
 
   // Close all tabs instead of navigating as the embedded_test_server will
   // hang waiting for loads to finish when we have an unfinished
@@ -360,7 +360,7 @@
 
   // Verify unfinished resource recorded when page is destroyed.
   histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Subframe.AdResource", 2);
+      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 2);
 
   histogram_tester.ExpectBucketCount(
       "PageLoad.Clients.Ads.Resources.Bytes.Total", 4, 1);
@@ -375,6 +375,51 @@
       "PageLoad.Clients.Ads.Resources.Bytes.Unfinished", 1, 1);
 }
 
+// Verify that per-resource metrics are reported for cached resources and
+// resources loaded by the network.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       RecordedCacheResourceMetrics) {
+  base::HistogramTester histogram_tester;
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("create_frame.js")});
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
+
+  // Wait for the favicon to be fetched.
+  waiter->AddMinimumCompleteResourcesExpectation(2);
+  waiter->Wait();
+
+  // All resources should have been loaded by network.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 2);
+
+  // Open a new tab and navigate so that resources are fetched via the disk
+  // cache. Navigating to the same URL in the same tab triggers a refresh which
+  // will not check the disk cache.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+          ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  waiter = CreateAdsPageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
+
+  // Wait for the resource to be fetched.
+  waiter->AddMinimumCompleteResourcesExpectation(1);
+  waiter->Wait();
+
+  // Resource should be recorded as loaded from the cache. Favicon not
+  // fetched this time.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Cache.Mainframe.VanillaResource", 1);
+}
+
 // Verify that Mime type metrics are recorded correctly.
 IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
                        RecordedMimeMetrics) {
@@ -399,15 +444,21 @@
   // Close all tabs to log metrics, as the video resource request is incomplete.
   browser()->tab_strip_model()->CloseAllTabs();
 
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.HTML", 1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.CSS", 1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.JS", 3);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.HTML",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.CSS",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.JS",
+                                    3);
 
   // Note: png and video/webm mime types are not set explicitly by the
   // embedded_test_server.
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.Image", 1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.Video", 1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Mime.Other", 1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Image",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Video",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Other",
+                                    1);
 
   // Verify UKM Metrics recorded.
   auto entries =
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index fcaf2a1a..35800552 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1075,16 +1075,23 @@
   if (!PasswordAccessoryController::AllowedForWebContents(web_contents())) {
     return;  // No need to even create the bridge if it's not going to be used.
   }
-  if (is_fillable) {  // If not fillable, update existing an accessory only.
+  if (is_password_field) {
+    DCHECK(is_fillable);
     PasswordAccessoryController::CreateForWebContents(web_contents());
-  }
-  PasswordAccessoryController* accessory =
-      PasswordAccessoryController::FromWebContents(web_contents());
-  if (accessory) {
+    PasswordAccessoryController* accessory =
+        PasswordAccessoryController::FromWebContents(web_contents());
     accessory->RefreshSuggestionsForField(
         password_manager_driver_bindings_.GetCurrentTargetFrame()
             ->GetLastCommittedOrigin(),
         is_fillable, is_password_field);
+    accessory->ShowWhenKeyboardIsVisible();
+  } else {
+    // If not a password field, only update the accessory if one exists.
+    PasswordAccessoryController* accessory =
+        PasswordAccessoryController::FromWebContents(web_contents());
+    if (accessory) {
+      accessory->Hide();
+    }
   }
 #endif  // defined(OS_ANDROID)
 }
diff --git a/chrome/browser/password_manager/password_accessory_controller.cc b/chrome/browser/password_manager/password_accessory_controller.cc
index 0bb11d7..22e566a 100644
--- a/chrome/browser/password_manager/password_accessory_controller.cc
+++ b/chrome/browser/password_manager/password_accessory_controller.cc
@@ -191,9 +191,7 @@
   for (const auto& pair : best_matches) {
     const PasswordForm* form = pair.second;
     suggestions->emplace_back(form->password_value, GetDisplayUsername(*form),
-                              form->username_value.empty()
-                                  ? Item::Type::NON_INTERACTIVE_SUGGESTION
-                                  : Item::Type::SUGGESTION);
+                              Item::Type::NON_INTERACTIVE_SUGGESTION);
   }
 }
 
@@ -264,6 +262,14 @@
   origin_suggestions_.clear();
 }
 
+void PasswordAccessoryController::ShowWhenKeyboardIsVisible() {
+  view_->ShowWhenKeyboardIsVisible();
+}
+
+void PasswordAccessoryController::Hide() {
+  view_->Hide();
+}
+
 void PasswordAccessoryController::GetFavicon(
     int desired_size_in_pixel,
     base::OnceCallback<void(const gfx::Image&)> icon_callback) {
diff --git a/chrome/browser/password_manager/password_accessory_controller.h b/chrome/browser/password_manager/password_accessory_controller.h
index c1b3b49..923bebc 100644
--- a/chrome/browser/password_manager/password_accessory_controller.h
+++ b/chrome/browser/password_manager/password_accessory_controller.h
@@ -102,6 +102,14 @@
   // Reacts to a navigation on the main frame, e.g. by clearing caches.
   void DidNavigateMainFrame();
 
+  // Requests to show the accessory bar. The accessory will only be shown
+  // when the keyboard becomes visible.
+  void ShowWhenKeyboardIsVisible();
+
+  // Requests to hide the accessory. This hides both the accessory sheet
+  // (if open) and the accessory bar.
+  void Hide();
+
   // --------------------------
   // Methods called by UI code:
   // --------------------------
diff --git a/chrome/browser/password_manager/password_accessory_controller_unittest.cc b/chrome/browser/password_manager/password_accessory_controller_unittest.cc
index bc3aa18..443cf71 100644
--- a/chrome/browser/password_manager/password_accessory_controller_unittest.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_unittest.cc
@@ -70,6 +70,8 @@
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
   MOCK_METHOD0(CloseAccessorySheet, void());
   MOCK_METHOD0(SwapSheetWithKeyboard, void());
+  MOCK_METHOD0(ShowWhenKeyboardIsVisible, void());
+  MOCK_METHOD0(Hide, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockPasswordAccessoryView);
@@ -355,7 +357,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -396,22 +398,22 @@
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
 
           MatchesItem(ASCIIToUTF16("Alf"), ASCIIToUTF16("Alf"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("PWD"), password_for_str("Alf"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
 
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
 
           MatchesItem(ASCIIToUTF16("Cat"), ASCIIToUTF16("Cat"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("M1@u"), password_for_str("Cat"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
 
           MatchesItem(ASCIIToUTF16("Zebra"), ASCIIToUTF16("Zebra"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("M3h"), password_for_str("Zebra"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -431,7 +433,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -541,7 +543,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -557,7 +559,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -575,7 +577,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -591,7 +593,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Alf"), ASCIIToUTF16("Alf"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("M3lm4k"), password_for_str("Alf"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -611,7 +613,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -643,7 +645,7 @@
       OnItemsAvailable(ElementsAre(
           IsTopDivider(), MatchesLabel(passwords_title_str(kExampleDomain)),
           MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
-                      ItemType::SUGGESTION),
+                      ItemType::NON_INTERACTIVE_SUGGESTION),
           MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"), true,
                       ItemType::NON_INTERACTIVE_SUGGESTION),
           IsDivider(), MatchesOption(manage_passwords_str()))));
@@ -855,3 +857,10 @@
       "PasswordGeneration.UserEvent",
       PasswordGenerationUserEvent::kPasswordRejectedInDialog, 1);
 }
+
+TEST_F(PasswordAccessoryControllerTest, RelaysShowAndHideKeyboardAccessory) {
+  EXPECT_CALL(*view(), ShowWhenKeyboardIsVisible());
+  controller()->ShowWhenKeyboardIsVisible();
+  EXPECT_CALL(*view(), Hide());
+  controller()->Hide();
+}
diff --git a/chrome/browser/password_manager/password_accessory_view_interface.h b/chrome/browser/password_manager/password_accessory_view_interface.h
index 21e734d..ee1ed94 100644
--- a/chrome/browser/password_manager/password_accessory_view_interface.h
+++ b/chrome/browser/password_manager/password_accessory_view_interface.h
@@ -81,6 +81,12 @@
   // Opens a keyboard which dismisses the sheet. NoOp without open sheet.
   virtual void SwapSheetWithKeyboard() = 0;
 
+  // Shows the accessory bar when the keyboard is also shown.
+  virtual void ShowWhenKeyboardIsVisible() = 0;
+
+  // Hides the accessory bar and the accessory sheet (if open).
+  virtual void Hide() = 0;
+
  private:
   friend class PasswordAccessoryController;
   // Factory function used to create a concrete instance of this view.
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 5c2f7b3..96295c7 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -687,6 +687,9 @@
   { key::kReportCrostiniUsageEnabled,
     crostini::prefs::kReportCrostiniUsageEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kNTLMShareAuthenticationEnabled,
+    prefs::kNTLMShareAuthenticationEnabled,
+    base::Value::Type::BOOLEAN },
 #endif  // defined(OS_CHROMEOS)
 
 // Metrics reporting is controlled by a platform specific policy for ChromeOS
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index e45c9db..3945233 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -127,7 +127,11 @@
 
 class PushMessagingBrowserTest : public InProcessBrowserTest {
  public:
-  PushMessagingBrowserTest() : gcm_service_(nullptr), gcm_driver_(nullptr) {}
+  PushMessagingBrowserTest()
+      : scoped_testing_factory_installer_(
+            base::BindRepeating(&gcm::FakeGCMProfileService::Build)),
+        gcm_service_(nullptr),
+        gcm_driver_(nullptr) {}
   ~PushMessagingBrowserTest() override {}
 
   // InProcessBrowserTest:
@@ -137,9 +141,6 @@
     https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
     ASSERT_TRUE(https_server_->Start());
 
-    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-        base::BindRepeating(&gcm::FakeGCMProfileService::Build));
-
     SiteEngagementScore::SetParamValuesForTesting();
     InProcessBrowserTest::SetUp();
   }
@@ -168,12 +169,6 @@
     LoadTestPage();
   }
 
-  void TearDown() override {
-    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
-        BrowserContextKeyedServiceFactory::TestingFactory());
-    InProcessBrowserTest::TearDown();
-  }
-
   void TearDownOnMainThread() override {
     notification_tester_.reset();
     InProcessBrowserTest::TearDownOnMainThread();
@@ -327,6 +322,9 @@
 
   virtual Browser* GetBrowser() const { return browser(); }
 
+  gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
+      scoped_testing_factory_installer_;
+
   gcm::FakeGCMProfileService* gcm_service_;
   instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
   base::HistogramTester histogram_tester_;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
index bbb5f1fd..5c63bc8 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
@@ -204,10 +204,6 @@
 
 Volume::~Volume() {
   worker_.Join();
-
-  if (volume_archive_) {
-    volume_archive_->Cleanup();
-  }
 }
 
 bool Volume::Init() {
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
index 4629ef3f..64e079a9 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
@@ -29,10 +29,9 @@
   };
 
   // Initializes VolumeArchive. Should be called only once.
-  // In case of any errors call VolumeArchive::Cleanup and the error message can
-  // be obtained with VolumeArchive::error_message(). Encoding is the default
-  // encoding. Note, that other encoding may be used if specified in the
-  // archive file.
+  // In case of any errors, the error message can be obtained with
+  // VolumeArchive::error_message(). Encoding is the default encoding. Note,
+  // that other encoding may be used if specified in the archive file.
   virtual bool Init(const std::string& encoding) = 0;
 
   // Gets the next header. If path_name is set to nullptr, then there are no
@@ -82,11 +81,6 @@
   // buffer.
   virtual void MaybeDecompressAhead() = 0;
 
-  // Cleans all resources. Should be called only once. Returns true if
-  // successful. In case of failure the error message can be obtained with
-  // VolumeArchive::error_message().
-  virtual bool Cleanup() = 0;
-
   VolumeReader* reader() const { return reader_.get(); }
   std::string error_message() const { return error_message_; }
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
index 3031aa7..debbf4ca 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
@@ -21,7 +21,6 @@
 const char kArchiveNextHeaderError[] =
     "Failed to open current file in archive.";
 const char kArchiveReadDataError[] = "Failed to read archive data.";
-const char kArchiveReadFreeError[] = "Failed to close archive.";
 
 // The size of the buffer used to skip unnecessary data. Should be positive and
 // UINT16_MAX or less. unzReadCurrentFile in third_party/minizip/src/unzip.c
@@ -225,7 +224,11 @@
       decompressed_error_(false) {}
 
 VolumeArchiveMinizip::~VolumeArchiveMinizip() {
-  Cleanup();
+  if (zip_file_) {
+    if (unzClose(zip_file_) != UNZ_OK) {
+      LOG(WARNING) << "Failed to close archive.";
+    }
+  }
 }
 
 bool VolumeArchiveMinizip::Init(const std::string& encoding) {
@@ -489,20 +492,6 @@
   decompressed_data_size_ = bytes_read;
 }
 
-bool VolumeArchiveMinizip::Cleanup() {
-  bool returnValue = true;
-  if (zip_file_) {
-    if (unzClose(zip_file_) != UNZ_OK) {
-      set_error_message(kArchiveReadFreeError);
-      returnValue = false;
-    }
-  }
-  zip_file_ = nullptr;
-  password_cache_.reset();
-
-  return returnValue;
-}
-
 int64_t VolumeArchiveMinizip::ReadData(int64_t offset,
                                        int64_t length,
                                        const char** buffer) {
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
index 0a91456..7196fae 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
@@ -70,9 +70,6 @@
   // See volume_archive_interface.h.
   void MaybeDecompressAhead() override;
 
-  // See volume_archive_interface.h.
-  bool Cleanup() override;
-
   int64_t reader_data_size() const { return reader_data_size_; }
 
   // Custom functions need to access private variables of
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index d83c432..5e9fa228 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -2681,8 +2681,7 @@
 
 TEST_F(DownloadProtectionServiceTest,
        CheckNotExtendedReportedDisabledDoesNotSendFeedback) {
-  PrefService* prefs = profile_->GetPrefs();
-  prefs->SetBoolean(GetExtendedReportingPrefName(*prefs), false);
+  SetExtendedReportingPreference(false);
 
   NiceMockDownloadItem item;
   EXPECT_FALSE(download_service_->MaybeBeginFeedbackForDownload(
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index c12e437e..f2819dcb 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -444,11 +444,11 @@
       ExpectLatestWindowOpenUkmEntry(
           ukm_recorder, expected_num_entries, false /* from_main_frame */,
           main_frame_url, ad_frame /* from_ad_subframe */,
-          false /* from_ad_script */);
+          ad_frame /* from_ad_script */);
       ExpectWindowOpenUmaStatus(
-          histogram_tester, 0 /* adscript_adframe */,
-          expected_num_from_ad_subframe /* nonadscript_adframe */,
-          0 /* adscript_nonadframe */,
+          histogram_tester,
+          expected_num_from_ad_subframe /* adscript_adframe */,
+          0 /* nonadscript_adframe */, 0 /* adscript_nonadframe */,
           expected_num_entries -
               expected_num_from_ad_subframe /* nonadscript_nonadframe */);
     }
diff --git a/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc b/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
index ac71bf2..86edd23a 100644
--- a/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc
@@ -5,7 +5,9 @@
 #include <stddef.h>
 
 #include "base/macros.h"
+#include "base/run_loop.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/test/bind_test_util.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
 #include "chrome/browser/sync/test/integration/feature_toggler.h"
@@ -15,6 +17,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/test/fake_server/fake_server.h"
 
@@ -22,6 +25,11 @@
 
 using sync_pb::HistoryDeleteDirectiveSpecifics;
 
+int64_t TimeToUnixUsec(base::Time time) {
+  DCHECK(!time.is_null());
+  return (time - base::Time::UnixEpoch()).InMicroseconds();
+}
+
 // Allows to wait until the number of server-side entities is equal to a
 // expected number.
 class HistoryDeleteDirectivesEqualityChecker
@@ -76,6 +84,28 @@
         .Wait();
   }
 
+  // Uses HistoryService to look up whether any history entry exists that
+  // exactly matches timestamp |time|.
+  bool LookupLocalHistoryEntry(base::Time time) {
+    history::HistoryService* history_service =
+        HistoryServiceFactory::GetForProfileWithoutCreating(GetProfile(0));
+
+    bool exists = false;
+    base::RunLoop loop;
+    base::CancelableTaskTracker task_tracker;
+    history_service->GetHistoryCount(
+        /*begin_time=*/time,
+        /*end_time=*/time + base::TimeDelta::FromMicroseconds(1),
+        base::BindLambdaForTesting([&](history::HistoryCountResult result) {
+          ASSERT_TRUE(result.success);
+          exists = (result.count != 0);
+          loop.Quit();
+        }),
+        &task_tracker);
+    loop.Run();
+    return exists;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientHistoryDeleteDirectivesSyncTest);
 };
@@ -101,6 +131,48 @@
   EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
 }
 
+IN_PROC_BROWSER_TEST_P(SingleClientHistoryDeleteDirectivesSyncTest,
+                       ShouldProcessDeleteDirectiveDuringStartup) {
+  const GURL kPageUrl = GURL("http://foo.com");
+  const base::Time kHistoryEntryTime = base::Time::Now();
+
+  ASSERT_TRUE(SetupClients());
+
+  // Initially (before sync starts) there is a local history entry.
+  history::HistoryService* history_service =
+      HistoryServiceFactory::GetForProfileWithoutCreating(GetProfile(0));
+  history_service->AddPage(kPageUrl, kHistoryEntryTime,
+                           history::SOURCE_BROWSED);
+
+  // Initially (before sync starts) there is a remote delete directive.
+  sync_pb::EntitySpecifics specifics;
+  sync_pb::TimeRangeDirective* time_range_directive =
+      specifics.mutable_history_delete_directive()
+          ->mutable_time_range_directive();
+  time_range_directive->set_start_time_usec(TimeToUnixUsec(kHistoryEntryTime));
+  time_range_directive->set_end_time_usec(TimeToUnixUsec(kHistoryEntryTime) +
+                                          1);
+
+  fake_server_->InjectEntity(
+      syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
+          "name", specifics, /*creation_time=*/0, /*last_modified_time=*/0));
+  EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
+
+  // Verify history exists prior to starting sync.
+  ASSERT_TRUE(LookupLocalHistoryEntry(kHistoryEntryTime));
+
+  ASSERT_TRUE(SetupSync());
+
+  // Verify history entry was deleted.
+  EXPECT_FALSE(LookupLocalHistoryEntry(kHistoryEntryTime));
+
+  // No deletion should be sent to the server. There's no way to verify this
+  // reliably in an integration test (i.e. it could eventually be deleted), but
+  // this should be a good approximation considering there was a round-trip to
+  // the history DB.
+  EXPECT_TRUE(WaitForHistoryDeleteDirectives(1));
+}
+
 INSTANTIATE_TEST_CASE_P(USS,
                         SingleClientHistoryDeleteDirectivesSyncTest,
                         ::testing::Values(false, true));
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index 53ee253..ce3daf49 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -71,6 +71,10 @@
   // notifies the provider's observer of the tasks removal.
   void ClearTaskForFrame(RenderFrameHost* render_frame_host);
 
+  // Same as |ClearTaskForFrame|, but for every descendant of
+  // |ancestor|.
+  void ClearTasksForDescendantsOf(RenderFrameHost* ancestor);
+
   // Calls |on_task| for each task managed by this WebContentsEntry.
   void ForEachTask(const base::Callback<void(RendererTask*)>& on_task);
 
@@ -164,7 +168,13 @@
 void WebContentsEntry::RenderFrameHostChanged(RenderFrameHost* old_host,
                                               RenderFrameHost* new_host) {
   DCHECK(new_host->IsCurrent());
+
+  // The navigating frame and its subframes are now pending deletion. Stop
+  // tracking them immediately rather than when they are destroyed. The order of
+  // deletion is important. The children must be removed first.
+  ClearTasksForDescendantsOf(old_host);
   ClearTaskForFrame(old_host);
+
   CreateTaskForFrame(new_host);
 }
 
@@ -358,6 +368,20 @@
   DCHECK(tasks_by_frames_.empty() == (main_frame_site_instance_ == nullptr));
 }
 
+void WebContentsEntry::ClearTasksForDescendantsOf(RenderFrameHost* ancestor) {
+  // 1) Collect descendants.
+  std::vector<RenderFrameHost*> descendants;
+  for (auto it : tasks_by_frames_) {
+    RenderFrameHost* frame = it.first;
+    if (frame->IsDescendantOf(ancestor))
+      descendants.push_back(frame);
+  }
+
+  // 2) Delete them.
+  for (RenderFrameHost* rfh : descendants)
+    ClearTaskForFrame(rfh);
+}
+
 void WebContentsEntry::ForEachTask(
     const base::Callback<void(RendererTask*)>& on_task) {
   for (const auto& pair : frames_by_site_instance_) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d64d67c..108b54de 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -855,6 +855,8 @@
       "global_error/global_error_service.h",
       "global_error/global_error_service_factory.cc",
       "global_error/global_error_service_factory.h",
+      "hats/hats_helper.cc",
+      "hats/hats_helper.h",
       "hats/hats_service.cc",
       "hats/hats_service.h",
       "hats/hats_service_factory.cc",
@@ -1759,6 +1761,10 @@
       "signin_view_controller.h",
       "signin_view_controller_delegate.cc",
       "signin_view_controller_delegate.h",
+      "views/close_bubble_on_tab_activation_helper.cc",
+      "views/close_bubble_on_tab_activation_helper.h",
+      "views/hats/hats_bubble_view.cc",
+      "views/hats/hats_bubble_view.h",
       "webui/discards/discards_ui.cc",
       "webui/discards/discards_ui.h",
       "webui/signin/inline_login_handler.cc",
@@ -1789,8 +1795,6 @@
       "sync/one_click_signin_sync_starter.h",
       "user_manager.cc",
       "user_manager.h",
-      "views/close_bubble_on_tab_activation_helper.cc",
-      "views/close_bubble_on_tab_activation_helper.h",
       "views/external_protocol_dialog.cc",
       "views/external_protocol_dialog.h",
       "views/profiles/badged_profile_photo.cc",
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index c0c7c211..f68bd5a7 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -280,6 +280,7 @@
   DOWNLOAD_OPEN_CONFIRMATION = 87,
   ARC_DATA_REMOVAL_CONFIRMATION = 88,
   CROSTINI_UPGRADE = 89,
+  HATS_BUBBLE = 90,
   MAX_VALUE
 };
 
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index c4bd804f..21ee7f04 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -404,6 +404,11 @@
       signin_metrics::AccessPoint access_point,
       bool is_source_keyboard) = 0;
 
+#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN) || \
+    defined(OS_LINUX)
+  virtual void ShowHatsBubbleFromAppMenuButton() = 0;
+#endif
+
   // Returns the height inset for RenderView when detached bookmark bar is
   // shown.  Invoked when a new RenderHostView is created for a non-NTP
   // navigation entry and the bookmark bar is detached.
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
index b401d81..698112bb 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -516,21 +516,13 @@
 }
 
 // Flaky on Linux, CrOS: http://crbug.com/159000
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-#define MAYBE_MouseLockSilentAfterTargetUnlock \
-  DISABLED_MouseLockSilentAfterTargetUnlock
-#elif defined(OS_WIN)
 // Flaky on Windows; see https://crbug.com/791539.
-#define MAYBE_MouseLockSilentAfterTargetUnlock \
-  DISABLED_MouseLockSilentAfterTargetUnlock
-#else
-#define MAYBE_MouseLockSilentAfterTargetUnlock MouseLockSilentAfterTargetUnlock
-#endif
+// Flaky on Mac: https://crbug.com/876617.
 
 // Tests mouse lock can be exited and re-entered by an application silently
 // with no UI distraction for users.
 IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest,
-                       MAYBE_MouseLockSilentAfterTargetUnlock) {
+                       DISABLED_MouseLockSilentAfterTargetUnlock) {
   SetWebContentsGrantedSilentMouseLockPermission();
   ASSERT_TRUE(embedded_test_server()->Start());
   ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/ui/hats/hats_helper.cc b/chrome/browser/ui/hats/hats_helper.cc
new file mode 100644
index 0000000..bfeee83
--- /dev/null
+++ b/chrome/browser/ui/hats/hats_helper.cc
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/hats/hats_helper.h"
+
+#include <memory>
+
+#include "base/task/post_task.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/ui/hats/hats_service.h"
+#include "chrome/browser/ui/hats/hats_service_factory.h"
+#include "chrome/common/chrome_features.h"
+#include "components/search/search.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+HatsHelper::HatsHelper(content::WebContents* web_contents)
+    : WebContentsObserver(web_contents), web_contents_(web_contents) {
+  DCHECK(search::IsInstantExtendedAPIEnabled());
+}
+
+HatsHelper::~HatsHelper() {}
+
+void HatsHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                               const GURL& /* validated_url */) {
+  if (!render_frame_host->GetParent() && search::IsInstantNTP(web_contents_)) {
+    HatsService* hats_service =
+        HatsServiceFactory::GetForProfile(profile(), true);
+
+    if (hats_service) {
+      hats_service->LaunchSatisfactionSurvey();
+    }
+  }
+}
+
+Profile* HatsHelper::profile() const {
+  return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+}
diff --git a/chrome/browser/ui/hats/hats_helper.h b/chrome/browser/ui/hats/hats_helper.h
new file mode 100644
index 0000000..2ecd6b7
--- /dev/null
+++ b/chrome/browser/ui/hats/hats_helper.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_HATS_HATS_HELPER_H_
+#define CHROME_BROWSER_UI_HATS_HATS_HELPER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+class Profile;
+
+// This is a browser side per tab helper that allows an entry trigger to
+// launch Happiness Tracking Surveys (HaTS)
+class HatsHelper : public content::WebContentsObserver,
+                   public content::WebContentsUserData<HatsHelper> {
+ public:
+  ~HatsHelper() override;
+
+ private:
+  friend class content::WebContentsUserData<HatsHelper>;
+
+  explicit HatsHelper(content::WebContents* web_contents);
+
+  // Overridden from contents::WebContentsObserver:
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
+
+  Profile* profile() const;
+
+  content::WebContents* web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(HatsHelper);
+};
+
+#endif  // CHROME_BROWSER_UI_HATS_HATS_HELPER_H_
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index 1fdb040..192af11 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -12,6 +12,7 @@
 #include "base/rand_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/chrome_features.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -29,6 +30,8 @@
 
 const char kHatsSurveyEnSiteIDDefault[] = "z4cctguzopq5x2ftal6vdgjrui";
 
+const char kHatsSurveyTriggerSatisfaction[] = "satisfaction";
+
 HatsFinchConfig CreateHatsFinchConfig() {
   HatsFinchConfig config;
   config.trigger = base::FeatureParam<std::string>(
@@ -59,7 +62,27 @@
 HatsService::HatsService(Profile* profile)
     : profile_(profile), hats_finch_config_(CreateHatsFinchConfig()) {}
 
-bool HatsService::ShouldShowSurvey() {
-  return (base::RandDouble() < hats_finch_config_.probability);
-  // TODO add pref checks to avoid too many surveys for a single profile
+void HatsService::LaunchSatisfactionSurvey() {
+  if (ShouldShowSurvey(kHatsSurveyTriggerSatisfaction)) {
+    Browser* browser = chrome::FindBrowserWithActiveWindow();
+    if (browser && browser->is_type_tabbed())
+      browser->window()->ShowHatsBubbleFromAppMenuButton();
+  }
 }
+
+bool HatsService::ShouldShowSurvey(const std::string& trigger) const {
+  if ((hats_finch_config_.trigger == trigger ||
+       hats_finch_config_.trigger == kHatsSurveyTriggerDefault) &&
+      !launch_hats_) {
+    if (base::RandDouble() < hats_finch_config_.probability) {
+      // we only want to ever show hats once per profile.
+      launch_hats_ = true;
+      return true;
+    }
+    // TODO add pref checks to avoid too many surveys for a single profile
+  }
+  return false;
+}
+
+// static
+bool HatsService::launch_hats_ = false;
diff --git a/chrome/browser/ui/hats/hats_service.h b/chrome/browser/ui/hats/hats_service.h
index 192b0cd8..a8f8db9 100644
--- a/chrome/browser/ui/hats/hats_service.h
+++ b/chrome/browser/ui/hats/hats_service.h
@@ -36,9 +36,18 @@
 class HatsService : public KeyedService {
  public:
   explicit HatsService(Profile* profile);
-  bool ShouldShowSurvey();
+
+  // This is the public function that will launch the "satisfaction" survey if
+  // it's appropriate.
+  void LaunchSatisfactionSurvey();
 
  private:
+  // This returns true is the survey trigger specified should be shown.
+  bool ShouldShowSurvey(const std::string& trigger) const;
+
+  // a temporary flag to ensure that hats is not launched multiple times
+  // TODO: replace with pref lookup
+  static bool launch_hats_;
   Profile* profile_;
   const HatsFinchConfig hats_finch_config_;
 
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index fdba4119f..4c73a9e 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -19,8 +19,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
-#include "chrome/browser/ui/hats/hats_service.h"
-#include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/clipboard_utils.h"
 #include "chrome/browser/ui/search/ntp_user_data_logger.h"
@@ -219,24 +217,8 @@
 
 void SearchTabHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
                                     const GURL& /* validated_url */) {
-  if (!render_frame_host->GetParent()) {
-    if (search::IsInstantNTP(web_contents_)) {
-      RecordNewTabLoadTime(web_contents_);
-
-#if !defined(OS_ANDROID)
-      if (base::FeatureList::IsEnabled(
-              features::kHappinessTrackingSurveysForDesktop)) {
-        HatsService* hats_service =
-            HatsServiceFactory::GetForProfile(profile(), true);
-
-        // In icognito mode, hats_service will be null.
-        if (hats_service && hats_service->ShouldShowSurvey()) {
-          // TODO launch bubble;
-        }
-      }
-#endif  // !defined(OS_ANDROID)
-    }
-  }
+  if (!render_frame_host->GetParent() && search::IsInstantNTP(web_contents_))
+    RecordNewTabLoadTime(web_contents_);
 }
 
 void SearchTabHelper::NavigationEntryCommitted(
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index d2d948e..b912d6f 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -118,6 +118,7 @@
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#include "chrome/browser/ui/hats/hats_helper.h"
 #endif
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
@@ -313,6 +314,13 @@
   metrics::DesktopSessionDurationObserver::CreateForWebContents(web_contents);
 #endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+  if (base::FeatureList::IsEnabled(
+          features::kHappinessTrackingSurveysForDesktop)) {
+    HatsHelper::CreateForWebContents(web_contents);
+  }
+#endif
+
 // --- Feature tab helpers behind flags ---
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 3df230e7..91ca3ec 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -84,6 +84,7 @@
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/frame/web_contents_close_handler.h"
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
+#include "chrome/browser/ui/views/hats/hats_bubble_view.h"
 #include "chrome/browser/ui/views/ime/ime_warning_bubble_view.h"
 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
@@ -2842,6 +2843,29 @@
 #endif
 }
 
+#if !defined(OS_ANDROID)
+void BrowserView::ShowHatsBubbleFromAppMenuButton() {
+  // Never show any HaTS bubble in Incognito.
+  if (!IsRegularOrGuestSession())
+    return;
+
+  AppMenuButton* app_menu_button =
+      toolbar_button_provider()->GetAppMenuButton();
+
+  // Do not show Hatsbubble if there is no avatar menu button to anchor from
+  if (!app_menu_button)
+    return;
+
+  DCHECK(app_menu_button->GetWidget());
+  views::BubbleDialogDelegateView* bubble = HatsBubbleView::CreateHatsBubble(
+      app_menu_button, browser(),
+      app_menu_button->GetWidget()->GetNativeView());
+
+  bubble->SetHighlightedButton(app_menu_button);
+  bubble->GetWidget()->Show();
+}
+#endif
+
 void BrowserView::ShowAvatarBubbleFromAvatarButton(
     AvatarBubbleMode mode,
     const signin::ManageAccountsParams& manage_accounts_params,
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 6c42eb8..bbebcffd 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -392,6 +392,7 @@
   FindBar* CreateFindBar() override;
   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
       override;
+  void ShowHatsBubbleFromAppMenuButton() override;
   void ShowAvatarBubbleFromAvatarButton(
       AvatarBubbleMode mode,
       const signin::ManageAccountsParams& manage_accounts_params,
diff --git a/chrome/browser/ui/views/hats/hats_browsertest.cc b/chrome/browser/ui/views/hats/hats_browsertest.cc
new file mode 100644
index 0000000..86cad24
--- /dev/null
+++ b/chrome/browser/ui/views/hats/hats_browsertest.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/hats/hats_bubble_view.h"
+
+class HatsBubbleTest : public DialogBrowserTest {
+ public:
+  HatsBubbleTest() {}
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    ASSERT_TRUE(browser()->is_type_tabbed());
+    BrowserView::GetBrowserViewForBrowser(InProcessBrowserTest::browser())
+        ->ShowHatsBubbleFromAppMenuButton();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HatsBubbleTest);
+};
+
+// Test that calls ShowUi("default").
+IN_PROC_BROWSER_TEST_F(HatsBubbleTest, InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/hats/hats_bubble_view.cc b/chrome/browser/ui/views/hats/hats_bubble_view.cc
new file mode 100644
index 0000000..2365474c
--- /dev/null
+++ b/chrome/browser/ui/views/hats/hats_bubble_view.cc
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/hats/hats_bubble_view.h"
+
+#include "base/macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/ui_features.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+// static
+HatsBubbleView* HatsBubbleView::instance_ = nullptr;
+
+views::BubbleDialogDelegateView* HatsBubbleView::GetHatsBubble() {
+  return instance_;
+}
+
+// static
+views::BubbleDialogDelegateView* HatsBubbleView::CreateHatsBubble(
+    AppMenuButton* anchor_button,
+    Browser* browser,
+    gfx::NativeView parent_view) {
+  base::RecordAction(base::UserMetricsAction("HatsBubble.Show"));
+
+  return new HatsBubbleView(anchor_button, browser, parent_view);
+}
+
+HatsBubbleView::HatsBubbleView(AppMenuButton* anchor_button,
+                               Browser* browser,
+                               gfx::NativeView parent_view)
+    : BubbleDialogDelegateView(anchor_button, views::BubbleBorder::TOP_LEFT),
+      close_bubble_helper_(this, browser) {
+  chrome::RecordDialogCreation(chrome::DialogIdentifier::HATS_BUBBLE);
+
+  set_parent_window(parent_view);
+
+  // TODO: this->AddChildView(WebUI)
+
+  views::BubbleDialogDelegateView::CreateBubble(this);
+
+  instance_ = this;
+}
+
+HatsBubbleView::~HatsBubbleView() {}
+
+int HatsBubbleView::GetDialogButtons() const {
+  return ui::DIALOG_BUTTON_NONE;
+}
+
+bool HatsBubbleView::ShouldShowCloseButton() const {
+  return true;
+}
+
+void HatsBubbleView::OnWidgetDestroying(views::Widget* widget) {
+  BubbleDialogDelegateView::OnWidgetDestroying(widget);
+  instance_ = nullptr;
+}
diff --git a/chrome/browser/ui/views/hats/hats_bubble_view.h b/chrome/browser/ui/views/hats/hats_bubble_view.h
new file mode 100644
index 0000000..1608412
--- /dev/null
+++ b/chrome/browser/ui/views/hats/hats_bubble_view.h
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_HATS_HATS_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_HATS_HATS_BUBBLE_VIEW_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/views/close_bubble_on_tab_activation_helper.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+class AppMenuButton;
+
+// This bubble view is displayed when a Happiness tracking survey is triggered.
+// It displays a WebUI that hosts the survey.
+class HatsBubbleView : public views::BubbleDialogDelegateView {
+ public:
+  // Returns a pointer to the Hats Bubble being shown. For testing only.
+  static views::BubbleDialogDelegateView* GetHatsBubble();
+  // Shows the bubble if one is not already showing.
+  static views::BubbleDialogDelegateView* CreateHatsBubble(
+      AppMenuButton* anchor_button,
+      Browser* browser,
+      gfx::NativeView parent_view);
+
+ protected:
+  // views::BubbleDialogDelegateView:
+  int GetDialogButtons() const override;
+  bool ShouldShowCloseButton() const override;
+  void OnWidgetDestroying(views::Widget* widget) override;
+
+ private:
+  HatsBubbleView(AppMenuButton* anchor_button,
+                 Browser* browser,
+                 gfx::NativeView parent_view);
+  ~HatsBubbleView() override;
+
+  static HatsBubbleView* instance_;
+  CloseBubbleOnTabActivationHelper close_bubble_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(HatsBubbleView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_HATS_HATS_BUBBLE_VIEW_H_
diff --git a/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc b/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
index 6e60b19..fb5c5de 100644
--- a/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
+++ b/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
@@ -32,10 +32,9 @@
                            prefs::kNetworkPredictionOptions, pref_service_);
   ObserveServicePrefChange(Service::kSafeBrowsing, prefs::kSafeBrowsingEnabled,
                            pref_service_);
-  ObserveServicePrefChange(
-      Service::kSafeBrowsingExtendedReporting,
-      safe_browsing::GetExtendedReportingPrefName(*pref_service_),
-      pref_service_);
+  ObserveServicePrefChange(Service::kSafeBrowsingExtendedReporting,
+                           prefs::kSafeBrowsingScoutReportingEnabled,
+                           pref_service_);
   ObserveServicePrefChange(Service::kSearchSuggest,
                            prefs::kSearchSuggestEnabled, pref_service_);
   ObserveServicePrefChange(Service::kSpellCheck,
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom
index fbff6e4..9e3a7bc 100644
--- a/chrome/common/page_load_metrics/page_load_metrics.mojom
+++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -167,6 +167,10 @@
   // is the aggregate of the |delta_bytes| from each timing update.
   int64 received_data_length = 0;
 
+  // The length of the response body for the resource before removing any
+  // content encodings.
+  int64 encoded_body_length = 0;
+
   // Whether this resource load has completed.
   bool is_complete;
 
@@ -183,6 +187,9 @@
   // Whether this resource was loaded in the top-level frame.
   bool is_main_frame_resource;
 
+  // Whether this resource was fetched from the http cache.
+  bool was_fetched_via_cache;
+
   // Mime type for the resource found in the network response header.
   string mime_type;
 };
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index f09813a5..6c7f8282 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -869,6 +869,12 @@
 // Last time that the kChildScreenTime pref was reset.
 const char kLastChildScreenTimeReset[] = "last_child_screen_time_reset";
 
+// Boolean pref indicating whether the NTLM authentication protocol should be
+// enabled when mounting an SMB share with a user credential by the Network File
+// Shares for Chrome OS feature.
+const char kNTLMShareAuthenticationEnabled[] =
+    "network_file_shares.ntlm_share_authentication.enabled";
+
 #endif  // defined(OS_CHROMEOS)
 
 // A boolean pref set to true if a Home button to open the Home pages should be
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index c6ddc29..d6f88f7 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -280,6 +280,7 @@
 extern const char kChildScreenTimeMilliseconds[];
 extern const char kLastChildScreenTimeSaved[];
 extern const char kLastChildScreenTimeReset[];
+extern const char kNTLMShareAuthenticationEnabled[];
 #endif  // defined(OS_CHROMEOS)
 extern const char kShowHomeButton[];
 extern const char kSpeechRecognitionFilterProfanities[];
diff --git a/chrome/install_static/install_constants.h b/chrome/install_static/install_constants.h
index dfccd1b..cc4a113 100644
--- a/chrome/install_static/install_constants.h
+++ b/chrome/install_static/install_constants.h
@@ -65,13 +65,17 @@
   // name registered with Default Programs on Windows.
   const wchar_t* base_app_name;
 
-  // The unsuffixed portion of the AppUserModelId. The AppUserModelId is used to
-  // group an app's windows together on the Windows taskbar along with its
+  // Used for the following:
+  // * The unsuffixed portion of the AppUserModelId. The AppUserModelId is used
+  // to group an app's windows together on the Windows taskbar along with its
   // corresponding shortcuts; see
   // https://msdn.microsoft.com/library/windows/desktop/dd378459.aspx for more
   // information. Use ShellUtil::GetBrowserModelId to get the suffixed value --
   // it is almost never correct to use the unsuffixed (base) portion of this id
   // directly.
+  // * The prefix for the Elevation Service Name. See
+  // install_static::GetElevationServiceDisplayName() and
+  // install_static::GetElevationServiceName().
   const wchar_t* base_app_id;
 
   // The prefix for the browser's ProgID. This prefix may be no more than 11
diff --git a/chrome/install_static/install_util.cc b/chrome/install_static/install_util.cc
index fb6bf84..b9024b28 100644
--- a/chrome/install_static/install_util.cc
+++ b/chrome/install_static/install_util.cc
@@ -405,6 +405,18 @@
   return InstallDetails::Get().elevator_clsid();
 }
 
+std::wstring GetElevationServiceName() {
+  std::wstring name = GetElevationServiceDisplayName();
+  name.erase(std::remove_if(name.begin(), name.end(), isspace), name.end());
+  return name;
+}
+
+std::wstring GetElevationServiceDisplayName() {
+  static constexpr wchar_t kElevationServiceDisplayName[] =
+      L" Elevation Service";
+  return GetBaseAppName() + kElevationServiceDisplayName;
+}
+
 std::wstring GetBaseAppName() {
   return InstallDetails::Get().mode().base_app_name;
 }
diff --git a/chrome/install_static/install_util.h b/chrome/install_static/install_util.h
index 1c2cd074..f04f980 100644
--- a/chrome/install_static/install_util.h
+++ b/chrome/install_static/install_util.h
@@ -109,8 +109,10 @@
 // the Windows OS.
 const CLSID& GetToastActivatorClsid();
 
-// The CLSID of the COM server that provides silent elevation functionality.
+// Return the Elevation Service CLSID, Name, and Display Name respectively.
 const CLSID& GetElevatorClsid();
+std::wstring GetElevationServiceName();
+std::wstring GetElevationServiceDisplayName();
 
 // Returns the unsuffixed application name of this program. This is the base of
 // the name registered with Default Programs. IMPORTANT: This must only be
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release
index 29da5235..01f69e5 100644
--- a/chrome/installer/mini_installer/chrome.release
+++ b/chrome/installer/mini_installer/chrome.release
@@ -23,6 +23,7 @@
 chrome_elf.dll: %(VersionDir)s\
 chrome_watcher.dll: %(VersionDir)s\
 d3dcompiler_47.dll: %(VersionDir)s\
+elevation_service.exe: %(VersionDir)s\
 eventlog_provider.dll: %(VersionDir)s\
 ffmpeg.dll: %(VersionDir)s\
 icudt.dll: %(VersionDir)s\
diff --git a/chrome/installer/setup/install_service_work_item.cc b/chrome/installer/setup/install_service_work_item.cc
index cbfb0f95..a6caeb9e 100644
--- a/chrome/installer/setup/install_service_work_item.cc
+++ b/chrome/installer/setup/install_service_work_item.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/installer/setup/install_service_work_item.h"
 
+#include "base/command_line.h"
 #include "chrome/installer/setup/install_service_work_item_impl.h"
 
 namespace installer {
@@ -11,7 +12,7 @@
 InstallServiceWorkItem::InstallServiceWorkItem(
     const base::string16& service_name,
     const base::string16& display_name,
-    const base::string16& service_cmd_line)
+    const base::CommandLine& service_cmd_line)
     : impl_(std::make_unique<InstallServiceWorkItemImpl>(service_name,
                                                          display_name,
                                                          service_cmd_line)) {}
@@ -26,4 +27,12 @@
   impl_->RollbackImpl();
 }
 
+// static
+bool InstallServiceWorkItem::DeleteService(const base::string16& service_name) {
+  return InstallServiceWorkItemImpl(
+             service_name, base::string16(),
+             base::CommandLine(base::CommandLine::NO_PROGRAM))
+      .DeleteServiceImpl();
+}
+
 }  // namespace installer
diff --git a/chrome/installer/setup/install_service_work_item.h b/chrome/installer/setup/install_service_work_item.h
index ec480bf1..75bcfd60 100644
--- a/chrome/installer/setup/install_service_work_item.h
+++ b/chrome/installer/setup/install_service_work_item.h
@@ -18,6 +18,10 @@
 #include "base/strings/string16.h"
 #include "chrome/installer/util/work_item.h"
 
+namespace base {
+class CommandLine;
+}  // namespace base
+
 namespace installer {
 
 class InstallServiceWorkItemImpl;
@@ -38,10 +42,12 @@
   // "C:\Program Files (x86)\Google\Chrome\ElevationService.exe" /svc
   InstallServiceWorkItem(const base::string16& service_name,
                          const base::string16& display_name,
-                         const base::string16& service_cmd_line);
+                         const base::CommandLine& service_cmd_line);
 
   ~InstallServiceWorkItem() override;
 
+  static bool DeleteService(const base::string16& service_name);
+
  private:
   friend class InstallServiceWorkItemTest;
 
diff --git a/chrome/installer/setup/install_service_work_item_impl.cc b/chrome/installer/setup/install_service_work_item_impl.cc
index 3d983e8..120ddc5 100644
--- a/chrome/installer/setup/install_service_work_item_impl.cc
+++ b/chrome/installer/setup/install_service_work_item_impl.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
@@ -59,10 +60,10 @@
 InstallServiceWorkItemImpl::InstallServiceWorkItemImpl(
     const base::string16& service_name,
     const base::string16& display_name,
-    const base::string16& service_cmd_line)
+    const base::CommandLine& service_cmd_line)
     : service_name_(service_name),
       display_name_(display_name),
-      service_cmd_line_(service_cmd_line),
+      service_cmd_line_(service_cmd_line.GetCommandLineString()),
       rollback_existing_service_(false),
       rollback_new_service_(false),
       original_service_still_exists_(false) {}
@@ -140,6 +141,34 @@
   LOG_IF(WARNING, !ReinstallOriginalService());
 }
 
+bool InstallServiceWorkItemImpl::DeleteServiceImpl() {
+  scm_.Set(::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT));
+  if (!scm_.IsValid()) {
+    DPLOG(ERROR) << "::OpenSCManager Failed";
+    return false;
+  }
+
+  if (!OpenService())
+    return false;
+
+  if (!DeleteCurrentService())
+    return false;
+
+  // If the service cannot be deleted, the service name value is not deleted.
+  // This is to allow for identifying that an existing instance of the service
+  // is still installed when a future install or upgrade runs.
+  base::win::RegKey key;
+  auto result = key.Open(HKEY_LOCAL_MACHINE,
+                         install_static::GetClientStateKeyPath().c_str(),
+                         KEY_SET_VALUE | KEY_WOW64_32KEY);
+  if (result != ERROR_SUCCESS)
+    return result == ERROR_FILE_NOT_FOUND || result == ERROR_PATH_NOT_FOUND;
+
+  result = key.DeleteValue(service_name_.c_str());
+  return result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND ||
+         result == ERROR_PATH_NOT_FOUND;
+}
+
 bool InstallServiceWorkItemImpl::IsServiceCorrectlyConfigured(
     const ServiceConfig& config) {
   return config.type == kServiceType &&
@@ -229,7 +258,7 @@
 base::string16 InstallServiceWorkItemImpl::GetCurrentServiceName() const {
   base::win::RegKey key;
 
-  LONG result = key.Open(HKEY_LOCAL_MACHINE,
+  auto result = key.Open(HKEY_LOCAL_MACHINE,
                          install_static::GetClientStateKeyPath().c_str(),
                          KEY_QUERY_VALUE | KEY_WOW64_32KEY);
   if (result != ERROR_SUCCESS)
diff --git a/chrome/installer/setup/install_service_work_item_impl.h b/chrome/installer/setup/install_service_work_item_impl.h
index 610e827..33d8862 100644
--- a/chrome/installer/setup/install_service_work_item_impl.h
+++ b/chrome/installer/setup/install_service_work_item_impl.h
@@ -13,6 +13,12 @@
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_types.h"
 
+namespace base {
+
+class CommandLine;
+
+}  // namespace base
+
 namespace installer {
 
 // Helper class for the implementation of InstallServiceWorkItem.
@@ -43,12 +49,13 @@
 
   InstallServiceWorkItemImpl(const base::string16& service_name,
                              const base::string16& display_name,
-                             const base::string16& service_cmd_line);
+                             const base::CommandLine& service_cmd_line);
 
   ~InstallServiceWorkItemImpl();
 
   bool DoImpl();
   void RollbackImpl();
+  bool DeleteServiceImpl();
 
   // Member functions that help with service installation or upgrades.
   bool IsServiceCorrectlyConfigured(const ServiceConfig& config);
diff --git a/chrome/installer/setup/install_service_work_item_unittest.cc b/chrome/installer/setup/install_service_work_item_unittest.cc
index 59bdf11..e06da7ad 100644
--- a/chrome/installer/setup/install_service_work_item_unittest.cc
+++ b/chrome/installer/setup/install_service_work_item_unittest.cc
@@ -8,6 +8,8 @@
 #include <memory>
 #include <vector>
 
+#include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/stl_util.h"
 #include "base/win/registry.h"
 #include "chrome/install_static/install_util.h"
@@ -20,7 +22,8 @@
 
 constexpr base::char16 kServiceName[] = L"InstallServiceWorkItemService";
 constexpr base::char16 kServiceDisplayName[] = L"InstallServiceWorkItemService";
-constexpr base::char16 kServiceCmdLine[] = L"c:\\windows\\system32\\cmd.exe";
+constexpr base::FilePath::CharType kServiceProgramPath[] =
+    FILE_PATH_LITERAL("c:\\windows\\system32\\cmd.exe");
 
 }  // namespace
 
@@ -63,7 +66,8 @@
 
 TEST_F(InstallServiceWorkItemTest, Do_FreshInstall) {
   auto item = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, kServiceCmdLine);
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
 
   ASSERT_TRUE(item->Do());
   EXPECT_TRUE(GetImpl(item.get())->OpenService());
@@ -73,16 +77,30 @@
   EXPECT_FALSE(GetImpl(item.get())->OpenService());
 }
 
+TEST_F(InstallServiceWorkItemTest, Do_FreshInstallThenDeleteService) {
+  auto item = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
+
+  ASSERT_TRUE(item->Do());
+  EXPECT_TRUE(GetImpl(item.get())->OpenService());
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+
+  EXPECT_TRUE(InstallServiceWorkItem::DeleteService(kServiceName));
+}
+
 TEST_F(InstallServiceWorkItemTest, Do_UpgradeNoChanges) {
   auto item = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, kServiceCmdLine);
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
   ASSERT_TRUE(item->Do());
 
   EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
 
   // Same command line:
   auto item_upgrade = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, kServiceCmdLine);
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
   EXPECT_TRUE(item_upgrade->Do());
 
   item_upgrade->Rollback();
@@ -93,14 +111,16 @@
 
 TEST_F(InstallServiceWorkItemTest, Do_UpgradeChangedCmdLine) {
   auto item = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, kServiceCmdLine);
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
   ASSERT_TRUE(item->Do());
 
   EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
 
   // New command line.
   auto item_upgrade = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, L"NewCmd.exe new cmd line");
+      kServiceName, kServiceDisplayName,
+      base::CommandLine::FromString(L"NewCmd.exe arg1 arg2"));
   EXPECT_TRUE(item_upgrade->Do());
 
   item_upgrade->Rollback();
@@ -119,7 +139,8 @@
                        install_static::GetClientStateKeyPath().c_str(),
                        KEY_WRITE | KEY_WOW64_32KEY));
   auto item = std::make_unique<InstallServiceWorkItem>(
-      kServiceName, kServiceDisplayName, kServiceCmdLine);
+      kServiceName, kServiceDisplayName,
+      base::CommandLine(base::FilePath(kServiceProgramPath)));
 
   EXPECT_STREQ(kServiceName,
                GetImpl(item.get())->GetCurrentServiceName().c_str());
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index b837e1b..84b6268b 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -36,6 +36,7 @@
 #include "chrome/install_static/install_details.h"
 #include "chrome/install_static/install_modes.h"
 #include "chrome/install_static/install_util.h"
+#include "chrome/installer/setup/install_service_work_item.h"
 #include "chrome/installer/setup/installer_state.h"
 #include "chrome/installer/setup/setup_constants.h"
 #include "chrome/installer/setup/setup_util.h"
@@ -429,6 +430,42 @@
       static_cast<DWORD>(consent), true);
 }
 
+// Adds work items to register the Elevation Service with Windows. Only for
+// system level installs.
+void AddElevationServiceWorkItems(const base::FilePath& elevation_service_path,
+                                  WorkItemList* list) {
+  DCHECK(::IsUserAnAdmin());
+  const HKEY root = HKEY_LOCAL_MACHINE;
+
+  if (elevation_service_path.empty()) {
+    LOG(DFATAL) << "The path to elevation_service.exe is invalid.";
+    return;
+  }
+
+  const base::string16 clsid_reg_path = GetElevationServiceClsidRegistryPath();
+  const base::string16 appid_reg_path = GetElevationServiceAppidRegistryPath();
+
+  // Delete any old registrations first, taking into account 32-bit -> 64-bit or
+  // 64-bit -> 32-bit migration.
+  for (const auto& reg_path : {clsid_reg_path, appid_reg_path}) {
+    for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY})
+      list->AddDeleteRegKeyWorkItem(root, reg_path, key_flag);
+  }
+
+  list->AddWorkItem(new InstallServiceWorkItem(
+      install_static::GetElevationServiceName(),
+      install_static::GetElevationServiceDisplayName(),
+      base::CommandLine(elevation_service_path)));
+
+  list->AddCreateRegKeyWorkItem(root, clsid_reg_path, WorkItem::kWow64Default);
+  list->AddSetRegValueWorkItem(root, clsid_reg_path, WorkItem::kWow64Default,
+                               L"AppID", GetElevationServiceGuid(L""), true);
+  list->AddCreateRegKeyWorkItem(root, appid_reg_path, WorkItem::kWow64Default);
+  list->AddSetRegValueWorkItem(root, appid_reg_path, WorkItem::kWow64Default,
+                               L"LocalService",
+                               install_static::GetElevationServiceName(), true);
+}
+
 }  // namespace
 
 // This method adds work items to create (or update) Chrome uninstall entry in
@@ -850,6 +887,11 @@
       installer_state.root_key(),
       GetNotificationHelperPath(target_path, new_version), install_list);
 
+  if (installer_state.system_install()) {
+    AddElevationServiceWorkItems(
+        GetElevationServicePath(target_path, new_version), install_list);
+  }
+
   InstallUtil::AddUpdateDowngradeVersionItem(
       installer_state.root_key(), current_version, new_version, install_list);
 
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 76294c6c..20efce4 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -858,4 +858,25 @@
       .Append(kNotificationHelperExe);
 }
 
+base::FilePath GetElevationServicePath(const base::FilePath& target_path,
+                                       const base::Version& version) {
+  return target_path.AppendASCII(version.GetString())
+      .Append(kElevationServiceExe);
+}
+
+base::string16 GetElevationServiceGuid(base::StringPiece16 prefix) {
+  base::string16 result =
+      InstallUtil::String16FromGUID(install_static::GetElevatorClsid());
+  result.insert(0, prefix.data(), prefix.size());
+  return result;
+}
+
+base::string16 GetElevationServiceClsidRegistryPath() {
+  return GetElevationServiceGuid(L"Software\\Classes\\CLSID\\");
+}
+
+base::string16 GetElevationServiceAppidRegistryPath() {
+  return GetElevationServiceGuid(L"Software\\Classes\\AppID\\");
+}
+
 }  // namespace installer
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index f1e921c..bf5e00a1 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -12,6 +12,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/optional.h"
@@ -157,6 +158,17 @@
 base::FilePath GetNotificationHelperPath(const base::FilePath& target_path,
                                          const base::Version& version);
 
+// Returns the file path to elevation_service.exe (in |version| directory).
+base::FilePath GetElevationServicePath(const base::FilePath& target_path,
+                                       const base::Version& version);
+
+// Returns the Elevation Service GUID prefixed with |prefix|.
+base::string16 GetElevationServiceGuid(base::StringPiece16 prefix);
+
+// Return the elevation service registry paths.
+base::string16 GetElevationServiceClsidRegistryPath();
+base::string16 GetElevationServiceAppidRegistryPath();
+
 }  // namespace installer
 
 #endif  // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index 189b970..75deeac 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -35,6 +35,7 @@
 #include "chrome/install_static/install_util.h"
 #include "chrome/installer/setup/brand_behaviors.h"
 #include "chrome/installer/setup/install.h"
+#include "chrome/installer/setup/install_service_work_item.h"
 #include "chrome/installer/setup/install_worker.h"
 #include "chrome/installer/setup/installer_state.h"
 #include "chrome/installer/setup/launch_chrome.h"
@@ -633,6 +634,19 @@
     LOG(DFATAL) << "Cannot retrieve the toast activator registry path";
   }
 
+  if (installer_state.system_install()) {
+    // Delete Software\Classes\CLSID and AppId\|elevation_service_clsid|.
+    const base::string16 clsid_reg_path =
+        GetElevationServiceClsidRegistryPath();
+    const base::string16 appid_reg_path =
+        GetElevationServiceAppidRegistryPath();
+    for (const auto& reg_path : {clsid_reg_path, appid_reg_path})
+      InstallUtil::DeleteRegistryKey(root, reg_path, WorkItem::kWow64Default);
+
+    LOG_IF(WARNING, !InstallServiceWorkItem::DeleteService(
+                        install_static::GetElevationServiceName()));
+  }
+
   // Delete all Start Menu Internet registrations that refer to this Chrome.
   {
     using base::win::RegistryKeyIterator;
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 5febc08..c53541e7 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -392,18 +392,23 @@
 }
 
 // static
-base::string16 InstallUtil::GetToastActivatorRegistryPath() {
-  // CLSID has a string format of "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",
-  // which contains 38 characters. The length is 39 to make space for the
-  // string terminator.
+base::string16 InstallUtil::String16FromGUID(const GUID& guid) {
+  // A GUID has a string format of "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",
+  // which contains 38 characters. The length is 39 inclusive of the string
+  // terminator.
   constexpr int kGuidLength = 39;
   base::string16 guid_string;
-  if (::StringFromGUID2(install_static::GetToastActivatorClsid(),
-                        base::WriteInto(&guid_string, kGuidLength),
-                        kGuidLength) != kGuidLength) {
-    return base::string16();
-  }
-  return L"Software\\Classes\\CLSID\\" + guid_string;
+
+  const int length_with_terminator = ::StringFromGUID2(
+      guid, base::WriteInto(&guid_string, kGuidLength), kGuidLength);
+  DCHECK_EQ(length_with_terminator, kGuidLength);
+  return guid_string;
+}
+
+// static
+base::string16 InstallUtil::GetToastActivatorRegistryPath() {
+  return L"Software\\Classes\\CLSID\\" +
+         String16FromGUID(install_static::GetToastActivatorClsid());
 }
 
 // static
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
index 9f6b3e6..332e153d 100644
--- a/chrome/installer/util/install_util.h
+++ b/chrome/installer/util/install_util.h
@@ -81,8 +81,10 @@
   // CLSID registered.
   static bool IsStartMenuShortcutWithActivatorGuidInstalled();
 
-  // Returns the toast activator registry path if found, or an empty string in
-  // case of error.
+  // Returns a string representation of |guid|.
+  static base::string16 String16FromGUID(const GUID& guid);
+
+  // Returns the toast activator registry path.
   static base::string16 GetToastActivatorRegistryPath();
 
   // Populates |path| with EULA sentinel file path. Returns false on error.
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index 2d4b8ac..38d6a94 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -205,6 +205,10 @@
 const wchar_t kUninstallDisplayNameField[] = L"DisplayName";
 const wchar_t kUninstallInstallationDate[] = L"installation_date";
 
+// Elevation Service constants.
+const base::FilePath::CharType kElevationServiceExe[] =
+    FILE_PATH_LITERAL("elevation_service.exe");
+
 // Google Update installer result API.
 const wchar_t kInstallerError[] = L"InstallerError";
 const wchar_t kInstallerExtraCode1[] = L"InstallerExtraCode1";
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index 9e39d2c5..a2b932c 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -10,6 +10,8 @@
 
 #include <stddef.h>
 
+#include "base/files/file_path.h"
+
 namespace installer {
 
 // Return status of installer. Values in this enum must not change. Always add
@@ -218,6 +220,9 @@
 extern const wchar_t kUninstallInstallationDate[];
 extern const wchar_t kUninstallStringField[];
 
+// Elevation Service constants.
+extern const base::FilePath::CharType kElevationServiceExe[];
+
 // Google Update installer result API.
 extern const wchar_t kInstallerError[];
 extern const wchar_t kInstallerExtraCode1[];
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.cc b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
index 2aa2752..091d32e 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.cc
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
@@ -19,7 +19,8 @@
       is_complete_(false),
       is_canceled_(false),
       reported_as_ad_resource_(false),
-      is_main_frame_resource_(false) {}
+      is_main_frame_resource_(false),
+      was_fetched_via_cache_(false) {}
 
 PageResourceDataUse::PageResourceDataUse(const PageResourceDataUse& other) =
     default;
@@ -34,6 +35,7 @@
   mime_type_ = response_head.mime_type;
   total_received_bytes_ = 0;
   last_update_bytes_ = 0;
+  was_fetched_via_cache_ = response_head.was_fetched_via_cache;
 }
 
 void PageResourceDataUse::DidReceiveTransferSizeUpdate(
@@ -45,6 +47,7 @@
     const network::URLLoaderCompletionStatus& status) {
   // Report the difference in received bytes.
   is_complete_ = true;
+  encoded_body_length_ = status.encoded_body_length;
   int64_t delta_bytes = status.encoded_data_length - total_received_bytes_;
   if (delta_bytes > 0) {
     total_received_bytes_ += delta_bytes;
@@ -89,6 +92,8 @@
   resource_data_update->reported_as_ad_resource = reported_as_ad_resource_;
   resource_data_update->is_main_frame_resource = is_main_frame_resource_;
   resource_data_update->mime_type = mime_type_;
+  resource_data_update->encoded_body_length = encoded_body_length_;
+  resource_data_update->was_fetched_via_cache = was_fetched_via_cache_;
   return resource_data_update;
 }
 }  // namespace page_load_metrics
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.h b/chrome/renderer/page_load_metrics/page_resource_data_use.h
index 278a783..aab2340c 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.h
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.h
@@ -62,11 +62,13 @@
 
   uint64_t total_received_bytes_;
   uint64_t last_update_bytes_;
+  uint64_t encoded_body_length_ = 0;
 
   bool is_complete_;
   bool is_canceled_;
   bool reported_as_ad_resource_;
   bool is_main_frame_resource_;
+  bool was_fetched_via_cache_;
 
   std::string mime_type_;
 
diff --git a/chrome/services/media_gallery_util/media_parser_android.cc b/chrome/services/media_gallery_util/media_parser_android.cc
index 7aa662c..b1317d9f 100644
--- a/chrome/services/media_gallery_util/media_parser_android.cc
+++ b/chrome/services/media_gallery_util/media_parser_android.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/services/media_gallery_util/media_parser_android.h"
 
+#include "base/optional.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "chrome/services/media_gallery_util/ipc_data_source.h"
@@ -28,7 +29,7 @@
 
   if (!frame) {
     std::move(video_frame_callback)
-        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+        .Run(false, chrome::mojom::VideoFrameData::New(), base::nullopt);
     return;
   }
 
@@ -47,7 +48,7 @@
     const media::VideoDecoderConfig& config) {
   if (!success || data.empty()) {
     std::move(video_frame_callback)
-        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+        .Run(false, chrome::mojom::VideoFrameData::New(), base::nullopt);
     return;
   }
 
@@ -67,7 +68,7 @@
   if (config.codec() != media::VideoCodec::kCodecVP8 &&
       config.codec() != media::VideoCodec::kCodecVP9) {
     std::move(video_frame_callback)
-        .Run(false, chrome::mojom::VideoFrameData::New(), config);
+        .Run(false, chrome::mojom::VideoFrameData::New(), base::nullopt);
     return;
   }
 
diff --git a/chrome/services/media_gallery_util/media_parser_android_unittest.cc b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
index 14b0a43..5b009985 100644
--- a/chrome/services/media_gallery_util/media_parser_android_unittest.cc
+++ b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind_helpers.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
@@ -25,7 +26,7 @@
 struct ExtractVideoFrameResult {
   bool success = false;
   chrome::mojom::VideoFrameDataPtr video_frame_data;
-  media::VideoDecoderConfig config;
+  base::Optional<media::VideoDecoderConfig> config;
 };
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
@@ -87,6 +88,7 @@
 
   void SetUp() override {
     parser_ = std::make_unique<MediaParserAndroid>(ref_factory_.CreateRef());
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
   }
 
   void TearDown() override { parser_.reset(); }
@@ -94,9 +96,10 @@
  protected:
   MediaParserAndroid* parser() { return parser_.get(); }
 
-  ExtractVideoFrameResult ExtractFrame(const std::string& file_name,
+  const base::FilePath& temp_dir() const { return temp_dir_.GetPath(); }
+
+  ExtractVideoFrameResult ExtractFrame(const base::FilePath& file_path,
                                        const std::string& mime_type) {
-    auto file_path = media::GetTestDataFilePath(file_name);
     int64_t size = 0;
     EXPECT_TRUE(base::GetFileSize(file_path, &size));
 
@@ -109,7 +112,7 @@
         mime_type, size, std::move(data_source_ptr),
         base::BindLambdaForTesting(
             [&](bool success, chrome::mojom::VideoFrameDataPtr video_frame_data,
-                const media::VideoDecoderConfig& config) {
+                const base::Optional<media::VideoDecoderConfig>& config) {
               result.success = success;
               result.video_frame_data = std::move(video_frame_data);
               result.config = config;
@@ -123,6 +126,7 @@
 
  private:
   std::unique_ptr<MediaParserAndroid> parser_;
+  base::ScopedTempDir temp_dir_;
 
   service_manager::ServiceContextRefFactory ref_factory_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -134,7 +138,8 @@
 // Test to verify an encoded video frame can be extracted for h264 codec video
 // file. Decoding needs to happen in other process.
 TEST_F(MediaParserAndroidTest, VideoFrameExtractionH264) {
-  auto result = ExtractFrame("bear.mp4", "video/mp4");
+  auto result =
+      ExtractFrame(media::GetTestDataFilePath("bear.mp4"), "video/mp4");
   EXPECT_TRUE(result.success);
   EXPECT_EQ(result.video_frame_data->which(),
             chrome::mojom::VideoFrameData::Tag::ENCODED_DATA);
@@ -146,7 +151,8 @@
 // Test to verify a decoded video frame can be extracted for vp8 codec video
 // file with YUV420 color format.
 TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8) {
-  auto result = ExtractFrame("bear-vp8-webvtt.webm", "video/webm");
+  auto result = ExtractFrame(media::GetTestDataFilePath("bear-vp8-webvtt.webm"),
+                             "video/webm");
   EXPECT_TRUE(result.success);
   EXPECT_EQ(result.video_frame_data->which(),
             chrome::mojom::VideoFrameData::Tag::DECODED_FRAME);
@@ -162,7 +168,8 @@
 // Test to verify a decoded video frame can be extracted for vp8 codec with
 // alpha plane.
 TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8WithAlphaPlane) {
-  auto result = ExtractFrame("bear-vp8a.webm", "video/webm");
+  auto result =
+      ExtractFrame(media::GetTestDataFilePath("bear-vp8a.webm"), "video/webm");
   EXPECT_TRUE(result.success);
 
   EXPECT_EQ(result.video_frame_data->which(),
@@ -176,4 +183,13 @@
             media::VideoFrame::StorageType::STORAGE_MOJO_SHARED_BUFFER);
 }
 
+// Test to verify frame extraction will fail on invalid video file.
+TEST_F(MediaParserAndroidTest, VideoFrameExtractionInvalidFile) {
+  base::FilePath dummy_file = temp_dir().AppendASCII("test.txt");
+  EXPECT_GT(base::WriteFile(dummy_file, "123", sizeof("123")), 0);
+
+  auto result = ExtractFrame(dummy_file, "video/webm");
+  EXPECT_FALSE(result.success);
+}
+
 }  // namespace
diff --git a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
index cb381b6..4ac236e8 100644
--- a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
+++ b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
@@ -38,7 +38,7 @@
                     MediaDataSource media_data_source)
       => (bool success,
           VideoFrameData frame_data,
-          media.mojom.VideoDecoderConfig config);
+          media.mojom.VideoDecoderConfig? config);
 
   // Validates the passed media file with sanity checks, and file decoding
   // for at most |decode_time| wall clock time. Returns |success| true if
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 35cf2b4d..7cfc24b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -255,6 +255,10 @@
       "../browser/chromeos/ownership/fake_owner_settings_service.h",
       "../browser/chromeos/settings/scoped_cros_settings_test_helper.cc",
       "../browser/chromeos/settings/scoped_cros_settings_test_helper.h",
+      "../browser/component_updater/fake_cros_component_manager.cc",
+      "../browser/component_updater/fake_cros_component_manager.h",
+      "base/browser_process_platform_part_test_api_chromeos.cc",
+      "base/browser_process_platform_part_test_api_chromeos.h",
       "base/default_ash_event_generator_delegate.cc",
       "base/default_ash_event_generator_delegate.h",
     ]
@@ -908,6 +912,7 @@
       "../browser/ui/views/chrome_cleaner_reboot_dialog_browsertest_win.cc",
       "../browser/ui/views/content_setting_bubble_contents_browsertest.cc",
       "../browser/ui/views/device_chooser_browsertest.cc",
+      "../browser/ui/views/hats/hats_browsertest.cc",
       "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
       "../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc",
       "../browser/ui/webauthn/authenticator_dialog_browsertest.cc",
@@ -3529,6 +3534,7 @@
       "../browser/extensions/api/file_system/file_system_api_unittest.cc",
       "../browser/extensions/api/identity/extension_token_key_unittest.cc",
       "../browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc",
+      "../browser/extensions/api/identity/identity_api_unittest.cc",
       "../browser/extensions/api/identity/identity_mint_queue_unittest.cc",
       "../browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc",
       "../browser/extensions/api/image_writer_private/operation_manager_unittest.cc",
diff --git a/chrome/test/base/browser_process_platform_part_test_api_chromeos.cc b/chrome/test/base/browser_process_platform_part_test_api_chromeos.cc
new file mode 100644
index 0000000..56a6017
--- /dev/null
+++ b/chrome/test/base/browser_process_platform_part_test_api_chromeos.cc
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
+
+#include <utility>
+
+#include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chrome/browser/component_updater/cros_component_manager.h"
+
+BrowserProcessPlatformPartTestApi::BrowserProcessPlatformPartTestApi(
+    BrowserProcessPlatformPart* platform_part)
+    : platform_part_(platform_part) {}
+
+BrowserProcessPlatformPartTestApi::~BrowserProcessPlatformPartTestApi() {
+  DCHECK(!platform_part_->using_testing_cros_component_manager_);
+}
+
+void BrowserProcessPlatformPartTestApi::InitializeCrosComponentManager(
+    std::unique_ptr<component_updater::CrOSComponentManager>
+        cros_component_manager) {
+  DCHECK(!platform_part_->using_testing_cros_component_manager_);
+  DCHECK(!platform_part_->cros_component_manager_);
+
+  platform_part_->using_testing_cros_component_manager_ = true;
+  platform_part_->cros_component_manager_ = std::move(cros_component_manager);
+}
+
+void BrowserProcessPlatformPartTestApi::ShutdownCrosComponentManager() {
+  DCHECK(platform_part_->using_testing_cros_component_manager_);
+  platform_part_->using_testing_cros_component_manager_ = false;
+  platform_part_->cros_component_manager_.reset();
+}
diff --git a/chrome/test/base/browser_process_platform_part_test_api_chromeos.h b/chrome/test/base/browser_process_platform_part_test_api_chromeos.h
new file mode 100644
index 0000000..e87d000
--- /dev/null
+++ b/chrome/test/base/browser_process_platform_part_test_api_chromeos.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_BASE_BROWSER_PROCESS_PLATFORM_PART_TEST_API_CHROMEOS_H_
+#define CHROME_TEST_BASE_BROWSER_PROCESS_PLATFORM_PART_TEST_API_CHROMEOS_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+class BrowserProcessPlatformPart;
+
+namespace component_updater {
+class CrOSComponentManager;
+}
+
+// Used to override parts of BrowserProcessPlatformParts in tests.
+class BrowserProcessPlatformPartTestApi {
+ public:
+  explicit BrowserProcessPlatformPartTestApi(
+      BrowserProcessPlatformPart* platform_part);
+  ~BrowserProcessPlatformPartTestApi();
+
+  // Initializes cros component manager for tests. Expects that cros component
+  // manager has not previously been initialized.
+  void InitializeCrosComponentManager(
+      std::unique_ptr<component_updater::CrOSComponentManager>
+          cros_component_manager);
+
+  // Shuts down the cros component manager set by
+  // InitializeCrosComponentManager().
+  void ShutdownCrosComponentManager();
+
+ private:
+  BrowserProcessPlatformPart* const platform_part_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPartTestApi);
+};
+
+#endif  // CHROME_TEST_BASE_BROWSER_PROCESS_PLATFORM_PART_TEST_API_CHROMEOS_H_
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 7e9cbb0..4d5e2b67 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/download/test_download_shelf.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -149,6 +150,12 @@
       const signin::ManageAccountsParams& manage_accounts_params,
       signin_metrics::AccessPoint access_point,
       bool is_source_keyboard) override {}
+
+#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN) || \
+    defined(OS_LINUX)
+  void ShowHatsBubbleFromAppMenuButton() override {}
+#endif
+
   int GetRenderViewHeightInsetWithDetachedBookmarkBar() override;
   void ExecuteExtensionCommand(const extensions::Extension* extension,
                                const extensions::Command& command) override;
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index c5c54c4..0f1889bc 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -176,6 +176,22 @@
     if (status.IsError())
       return Status(kUnknownError, "cannot get automation extension", status);
 
+    // The automation extension page has been loaded, but it might not be
+    // initialized yet. Wait for up to 10 seconds for a function on the page
+    // to become defined, as a signal that the page is initialized.
+    base::TimeTicks deadline =
+        base::TimeTicks::Now() + base::TimeDelta::FromSeconds(10);
+    while (base::TimeTicks::Now() < deadline) {
+      std::unique_ptr<base::Value> result;
+      status = web_view->EvaluateScript(
+          std::string(), "typeof launchApp === 'function'", &result);
+      if (status.IsError())
+        return Status(kUnknownError, "cannot get automation extension", status);
+      if (result->is_bool() && result->GetBool())
+        break;
+      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
+    }
+
     automation_extension_.reset(new AutomationExtension(std::move(web_view)));
   }
   *extension = automation_extension_.get();
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index f1d3074..4d9bcde 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -12,6 +12,7 @@
 
 ELEMENT_KEY_W3C = "element-6066-11e4-a52e-4f735466cecf"
 ELEMENT_KEY = "ELEMENT"
+MAX_RETRY_COUNT = 3
 
 class ChromeDriverException(Exception):
   pass
@@ -122,17 +123,32 @@
 class ChromeDriver(object):
   """Starts and controls a single Chrome instance on this machine."""
 
-  def __init__(self, server_url, chrome_binary=None, android_package=None,
-               android_activity=None, android_process=None,
-               android_use_running_app=None, chrome_switches=None,
-               chrome_extensions=None, chrome_log_path=None,
-               debugger_address=None, logging_prefs=None,
-               mobile_emulation=None, experimental_options=None,
-               download_dir=None, network_connection=None,
-               send_w3c_capability=None, send_w3c_request=None,
-               page_load_strategy=None, unexpected_alert_behaviour=None,
-               devtools_events_to_log=None, accept_insecure_certs=None,
-               timeouts=None, test_name=None):
+  retry_count = 0
+
+  def __init__(self, *args, **kwargs):
+    try:
+      self._InternalInit(*args, **kwargs)
+    except Exception as e:
+      if not e.message.startswith('timed out'):
+        raise
+      else:
+        if ChromeDriver.retry_count < MAX_RETRY_COUNT:
+          ChromeDriver.retry_count = ChromeDriver.retry_count + 1
+          self._InternalInit(*args, **kwargs)
+        else:
+          raise
+
+  def _InternalInit(self, server_url, chrome_binary=None, android_package=None,
+      android_activity=None, android_process=None,
+      android_use_running_app=None, chrome_switches=None,
+      chrome_extensions=None, chrome_log_path=None,
+      debugger_address=None, logging_prefs=None,
+      mobile_emulation=None, experimental_options=None,
+      download_dir=None, network_connection=None,
+      send_w3c_capability=None, send_w3c_request=None,
+      page_load_strategy=None, unexpected_alert_behaviour=None,
+      devtools_events_to_log=None, accept_insecure_certs=None,
+      timeouts=None, test_name=None):
     self._executor = command_executor.CommandExecutor(server_url)
     self.w3c_compliant = False
 
diff --git a/chrome/test/chromedriver/net/websocket.cc b/chrome/test/chromedriver/net/websocket.cc
index c8f01bd..1eacaf3 100644
--- a/chrome/test/chromedriver/net/websocket.cc
+++ b/chrome/test/chromedriver/net/websocket.cc
@@ -59,14 +59,17 @@
 
 }  // namespace
 
-WebSocket::WebSocket(const GURL& url, WebSocketListener* listener)
+WebSocket::WebSocket(const GURL& url,
+                     WebSocketListener* listener,
+                     size_t read_buffer_size)
     : url_(url),
       listener_(listener),
       state_(INITIALIZED),
       write_buffer_(base::MakeRefCounted<net::DrainableIOBuffer>(
           base::MakeRefCounted<net::IOBuffer>(0),
           0)),
-      read_buffer_(base::MakeRefCounted<net::IOBufferWithSize>(4096)) {}
+      read_buffer_(
+          base::MakeRefCounted<net::IOBufferWithSize>(read_buffer_size)) {}
 
 WebSocket::~WebSocket() {
   CHECK(thread_checker_.CalledOnValidThread());
diff --git a/chrome/test/chromedriver/net/websocket.h b/chrome/test/chromedriver/net/websocket.h
index afbc9932..e67e0f4 100644
--- a/chrome/test/chromedriver/net/websocket.h
+++ b/chrome/test/chromedriver/net/websocket.h
@@ -28,7 +28,9 @@
 class WebSocket {
  public:
   // |url| must be an IP v4/v6 literal, not a host name.
-  WebSocket(const GURL& url, WebSocketListener* listener);
+  WebSocket(const GURL& url,
+            WebSocketListener* listener,
+            size_t read_buffer_size = 4096);
   virtual ~WebSocket();
 
   // Initializes the WebSocket connection. Invokes the given callback with
diff --git a/chrome/test/chromedriver/net/websocket_unittest.cc b/chrome/test/chromedriver/net/websocket_unittest.cc
index 352a89f8..65690d7 100644
--- a/chrome/test/chromedriver/net/websocket_unittest.cc
+++ b/chrome/test/chromedriver/net/websocket_unittest.cc
@@ -91,7 +91,10 @@
   std::unique_ptr<WebSocket> CreateWebSocket(const GURL& url,
                                              WebSocketListener* listener) {
     int error;
-    std::unique_ptr<WebSocket> sock(new WebSocket(url, listener));
+    std::unique_ptr<WebSocket> sock(
+        read_buffer_size_ == 0
+            ? new WebSocket(url, listener)
+            : new WebSocket(url, listener, read_buffer_size_));
     base::RunLoop run_loop;
     sock->Connect(base::Bind(&OnConnectFinished, &run_loop, &error));
     loop_.task_runner()->PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
@@ -120,8 +123,11 @@
     run_loop.Run();
   }
 
+  void SetReadBufferSize(size_t size) { read_buffer_size_ = size; }
+
   base::MessageLoopForIO loop_;
   TestHttpServer server_;
+  size_t read_buffer_size_ = 0;
 };
 
 }  // namespace
@@ -195,6 +201,16 @@
   SendReceive(messages);
 }
 
+TEST_F(WebSocketTest, SendReceiveManyPacks) {
+  std::vector<std::string> messages;
+  // A message size of 1 << 16 crashes code with https://crbug.com/877105 bug
+  // on Linux and Windows, but a size of 1 << 17 is needed to cause crash on
+  // Mac. We use message size 1 << 18 for some extra margin to ensure bug repro.
+  messages.push_back(std::string(1 << 18, 'a'));
+  SetReadBufferSize(1);
+  SendReceive(messages);
+}
+
 TEST_F(WebSocketTest, SendReceiveMultiple) {
   std::vector<std::string> messages;
   messages.push_back("1");
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 9dcf8d6..8cdbecff 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -2984,6 +2984,14 @@
       self.CreateDriver('http://[::1]:' +
                                  str(chromedriver_server.GetPort()))
 
+
+# 'Z' in the beginning is to make test executed in the end of suite.
+class ZChromeStartRetryCountTest(unittest.TestCase):
+
+  def testChromeStartRetryCount(self):
+    self.assertEquals(0, chromedriver.ChromeDriver.retry_count,
+                      "Chrome was retried to start during suite execution")
+
 if __name__ == '__main__':
   parser = optparse.OptionParser()
   parser.add_option(
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 9dbe4798..7fef3a56 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3371,6 +3371,14 @@
     ]
   },
 
+  "NTLMShareAuthenticationEnabled": {
+    "os": ["chromeos"],
+    "test_policy": { "NTLMShareAuthenticationEnabled": true },
+    "pref_mappings": [
+      { "pref": "network_file_shares.ntlm_share_authentication.enabled" }
+    ]
+  },
+
   "----- Chrome OS device policies ---------------------------------------": {},
 
   "DevicePolicyRefreshRate": {
diff --git a/chrome/test/mini_installer/config/chrome_beta_installed.prop b/chrome/test/mini_installer/config/chrome_beta_installed.prop
index 9c4efb5..a03cc61 100644
--- a/chrome/test/mini_installer/config/chrome_beta_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_beta_installed.prop
@@ -50,6 +50,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_BETA": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_BETA": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_BETA$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_beta_not_installed.prop b/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
index 7ddd34a..00fe2108 100644
--- a/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
@@ -31,6 +31,12 @@
     },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_BETA": {
       "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_BETA": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_BETA": {
+      "exists": "forbidden"
     }
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_canary_installed.prop b/chrome/test/mini_installer/config/chrome_canary_installed.prop
index 75860b6..9224185 100644
--- a/chrome/test/mini_installer/config/chrome_canary_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_canary_installed.prop
@@ -50,6 +50,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_SXS$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_canary_not_installed.prop b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
index 1455f4bf..cd63c19 100644
--- a/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
@@ -31,6 +31,12 @@
     },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_SXS": {
       "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
     }
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_dev_installed.prop b/chrome/test/mini_installer/config/chrome_dev_installed.prop
index 7f3f82282..abe79429 100644
--- a/chrome/test/mini_installer/config/chrome_dev_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_dev_installed.prop
@@ -50,6 +50,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_DEV": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_DEV": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_DEV$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_dev_not_installed.prop b/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
index 1a0e0aa..1278f8b 100644
--- a/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
@@ -31,6 +31,12 @@
     },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_DEV": {
       "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_DEV": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_DEV": {
+      "exists": "forbidden"
     }
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_multi_system_installed.prop b/chrome/test/mini_installer/config/chrome_multi_system_installed.prop
index 1624cd7..7bba016 100644
--- a/chrome/test/mini_installer/config/chrome_multi_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_multi_system_installed.prop
@@ -62,6 +62,53 @@
         "data": "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "AppID": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATOR_CLSID"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "LocalService": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_NAME"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\$CHROME_ELEVATION_SERVICE_NAME": {
+      "exists": "required",
+      "values": {
+        "Type": {
+          "type": "DWORD",
+          "data": 16
+        },
+        "Start": {
+          "type": "DWORD",
+          "data": 3
+        },
+        "ErrorControl": {
+          "type": "DWORD",
+          "data": 1
+        },
+        "ImagePath": {
+          "type": "EXPAND_SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\elevation_service.exe\""
+        },
+        "DisplayName": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_DISPLAY_NAME"
+        },
+        "ObjectName": {
+          "type": "SZ",
+          "data": "LocalSystem"
+        }
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_SHORT_NAME": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_multi_user_installed.prop b/chrome/test/mini_installer/config/chrome_multi_user_installed.prop
index 56d1d77..08a1caa 100644
--- a/chrome/test/mini_installer/config/chrome_multi_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_multi_user_installed.prop
@@ -62,6 +62,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_system_installed.prop b/chrome/test/mini_installer/config/chrome_system_installed.prop
index f49b860..e4453a8 100644
--- a/chrome/test/mini_installer/config/chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_installed.prop
@@ -73,6 +73,53 @@
         "data": "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "AppID": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATOR_CLSID"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "LocalService": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_NAME"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\$CHROME_ELEVATION_SERVICE_NAME": {
+      "exists": "required",
+      "values": {
+        "Type": {
+          "type": "DWORD",
+          "data": 16
+        },
+        "Start": {
+          "type": "DWORD",
+          "data": 3
+        },
+        "ErrorControl": {
+          "type": "DWORD",
+          "data": 1
+        },
+        "ImagePath": {
+          "type": "EXPAND_SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\elevation_service.exe\""
+        },
+        "DisplayName": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_DISPLAY_NAME"
+        },
+        "ObjectName": {
+          "type": "SZ",
+          "data": "LocalSystem"
+        }
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_SHORT_NAME": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/chrome_system_not_installed.prop b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
index 580ffbe..60a6d0b 100644
--- a/chrome/test/mini_installer/config/chrome_system_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
@@ -37,6 +37,15 @@
     },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID": {
       "exists": "forbidden"
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\$CHROME_ELEVATION_SERVICE_NAME": {
+      "exists": "forbidden"
     }
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_user_installed.prop b/chrome/test/mini_installer/config/chrome_user_installed.prop
index b1ba621..acd6c87 100644
--- a/chrome/test/mini_installer/config/chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_installed.prop
@@ -58,6 +58,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/chrome_user_not_installed.prop b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
index ad44ddf..88e8a7a 100644
--- a/chrome/test/mini_installer/config/chrome_user_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
@@ -37,6 +37,12 @@
     },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID": {
       "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
     }
   }
 }
diff --git a/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop b/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
index f0d94b5..520e9de7 100644
--- a/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
@@ -42,6 +42,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID_SXS": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_SXS$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/config/previous_chrome_system_installed.prop b/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
index e3c47cb..8a84ac4 100644
--- a/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
@@ -71,6 +71,53 @@
         "data": "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "AppID": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATOR_CLSID"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "required",
+      "values": {
+        "LocalService": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_NAME"
+        }
+      }
+    },
+    "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\$CHROME_ELEVATION_SERVICE_NAME": {
+      "exists": "required",
+      "values": {
+        "Type": {
+          "type": "DWORD",
+          "data": 16
+        },
+        "Start": {
+          "type": "DWORD",
+          "data": 3
+        },
+        "ErrorControl": {
+          "type": "DWORD",
+          "data": 1
+        },
+        "ImagePath": {
+          "type": "EXPAND_SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\elevation_service.exe\""
+        },
+        "DisplayName": {
+          "type": "SZ",
+          "data": "$CHROME_ELEVATION_SERVICE_DISPLAY_NAME"
+        },
+        "ObjectName": {
+          "type": "SZ",
+          "data": "LocalSystem"
+        }
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_SHORT_NAME": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/previous_chrome_user_installed.prop b/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
index 8fdcbf4..9278d83 100644
--- a/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
@@ -56,6 +56,12 @@
         "data": "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\notification_helper.exe"
       }
     },
+    "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\AppID\\$CHROME_ELEVATOR_CLSID": {
+      "exists": "forbidden"
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     }
diff --git a/chrome/test/mini_installer/variable_expander.py b/chrome/test/mini_installer/variable_expander.py
index 1eeea332..ea3bade7 100644
--- a/chrome/test/mini_installer/variable_expander.py
+++ b/chrome/test/mini_installer/variable_expander.py
@@ -121,6 +121,25 @@
             Chrome Dev.
         * $CHROME_TOAST_ACTIVATOR_CLSID_SXS: NotificationActivator's CLSID for
             Chrome SxS.
+        * $CHROME_ELEVATOR_CLSID: Elevator Service CLSID for Chrome.
+        * $CHROME_ELEVATOR_CLSID_BETA: Elevator Service CLSID for Chrome Beta.
+        * $CHROME_ELEVATOR_CLSID_DEV: Elevator Service CLSID for Chrome Dev.
+        * $CHROME_ELEVATOR_CLSID_SXS: Elevator Service CLSID for Chrome SxS.
+        * $CHROME_ELEVATION_SERVICE_NAME: Elevation Service Name for Chrome.
+        * $CHROME_ELEVATION_SERVICE_NAME_BETA: Elevation Service Name for Chrome
+            Beta.
+        * $CHROME_ELEVATION_SERVICE_NAME_DEV: Elevation Service Name for Chrome
+            Dev.
+        * $CHROME_ELEVATION_SERVICE_NAME_SXS: Elevation Service Name for Chrome
+            SxS.
+        * $CHROME_ELEVATION_SERVICE_DISPLAY_NAME: Elevation Service Display Name
+            for Chrome.
+        * $CHROME_ELEVATION_SERVICE_DISPLAY_NAME_BETA: Elevation Service Display
+            Name for Chrome Beta.
+        * $CHROME_ELEVATION_SERVICE_DISPLAY_NAME_DEV: Elevation Service Display
+            Name for Chrome Dev.
+        * $CHROME_ELEVATION_SERVICE_DISPLAY_NAME_SXS: Elevation Service Display
+            Name for Chrome SxS.
 
     Args:
       mini_installer_path: The path to a mini_installer.
@@ -206,6 +225,29 @@
             '{F01C03EB-D431-4C83-8D7A-902771E732FA}'),
           'CHROME_TOAST_ACTIVATOR_CLSID_SXS': (
             '{FA372A6E-149F-4E95-832D-8F698D40AD7F}'),
+          'CHROME_ELEVATOR_CLSID': ('{708860E0-F641-4611-8895-7D867DD3675B}'),
+          'CHROME_ELEVATOR_CLSID_BETA': (
+            '{DD2646BA-3707-4BF8-B9A7-038691A68FC2}'),
+          'CHROME_ELEVATOR_CLSID_DEV': (
+            '{DA7FDCA5-2CAA-4637-AA17-0740584DE7DA}'),
+          'CHROME_ELEVATOR_CLSID_SXS': (
+            '{704C2872-2049-435E-A469-0A534313C42B}'),
+          'CHROME_ELEVATION_SERVICE_NAME': (
+            'GoogleChromeElevationService'),
+          'CHROME_ELEVATION_SERVICE_NAME_BETA': (
+            'GoogleChromeBetaElevationService'),
+          'CHROME_ELEVATION_SERVICE_NAME_DEV': (
+            'GoogleChromeDevElevationService'),
+          'CHROME_ELEVATION_SERVICE_NAME_SXS': (
+            'GoogleChromeCanaryElevationService'),
+          'CHROME_ELEVATION_SERVICE_DISPLAY_NAME': (
+            'Google Chrome Elevation Service'),
+          'CHROME_ELEVATION_SERVICE_DISPLAY_NAME_BETA': (
+            'Google Chrome Beta Elevation Service'),
+          'CHROME_ELEVATION_SERVICE_DISPLAY_NAME_DEV': (
+            'Google Chrome Dev Elevation Service'),
+          'CHROME_ELEVATION_SERVICE_DISPLAY_NAME_SXS': (
+            'Google Chrome Canary Elevation Service'),
       })
     elif mini_installer_product_name == 'Chromium Installer':
       self._variable_mapping.update({
@@ -219,6 +261,10 @@
           'CHROME_CLIENT_STATE_KEY': 'Software\\Chromium',
           'CHROME_TOAST_ACTIVATOR_CLSID': (
             '{635EFA6F-08D6-4EC9-BD14-8A0FDE975159}'),
+          'CHROME_ELEVATOR_CLSID': ('{D133B120-6DB4-4D6B-8BFE-83BF8CA1B1B0}'),
+          'CHROME_ELEVATION_SERVICE_NAME': 'ChromiumElevationService',
+          'CHROME_ELEVATION_SERVICE_DISPLAY_NAME': (
+            'Chromium Elevation Service'),
       })
     else:
       raise KeyError("Unknown mini_installer product name '%s'" %
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index ac6ff1e..888b3f1d 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -803,4 +803,15 @@
     'archive': 'chromedriver_win32-syms.zip',
     'optional': ['official'],
   },
+  # Elevation service files:
+  {
+    'filename': 'elevation_service.exe',
+    'buildtype': ['dev', 'official'],
+    'filegroup': ['default', 'symsrc'],
+  },
+  {
+    'filename': 'elevation_service.exe.pdb',
+    'buildtype': ['dev', 'official'],
+    'archive': 'chrome-win32-syms.zip',
+  },
 ]
diff --git a/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.cc b/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
index b84c88a..7cb0f65 100644
--- a/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
+++ b/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
@@ -112,12 +112,25 @@
   return static_cast<ProximityThreshold>(pref_value);
 }
 
+bool ProximityAuthLocalStatePrefManager::IsChromeOSLoginAllowed() const {
+  bool pref_value;
+  const base::DictionaryValue* user_prefs = GetActiveUserPrefsDictionary();
+  if (!user_prefs ||
+      !user_prefs->GetBooleanWithoutPathExpansion(
+          chromeos::multidevice_setup::kSmartLockSigninAllowedPrefName,
+          &pref_value)) {
+    PA_LOG(INFO) << "Failed to get is_chrome_login_allowed, not disallowing";
+    return true;
+  }
+  return pref_value;
+}
+
 void ProximityAuthLocalStatePrefManager::SetIsChromeOSLoginEnabled(
     bool is_enabled) {
   NOTREACHED();
 }
 
-bool ProximityAuthLocalStatePrefManager::IsChromeOSLoginEnabled() {
+bool ProximityAuthLocalStatePrefManager::IsChromeOSLoginEnabled() const {
   bool pref_value;
   const base::DictionaryValue* user_prefs = GetActiveUserPrefsDictionary();
   if (!user_prefs ||
diff --git a/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.h b/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.h
index 1fb3424d..c006b99e 100644
--- a/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.h
+++ b/chromeos/components/proximity_auth/proximity_auth_local_state_pref_manager.h
@@ -46,7 +46,8 @@
   bool IsEasyUnlockEnabled() const override;
   bool IsEasyUnlockEnabledStateSet() const override;
   ProximityThreshold GetProximityThreshold() const override;
-  bool IsChromeOSLoginEnabled() override;
+  bool IsChromeOSLoginAllowed() const override;
+  bool IsChromeOSLoginEnabled() const override;
 
  private:
   // ProximityAuthPrefManager:
diff --git a/chromeos/components/proximity_auth/proximity_auth_pref_manager.h b/chromeos/components/proximity_auth/proximity_auth_pref_manager.h
index e98bcbb..8b60199 100644
--- a/chromeos/components/proximity_auth/proximity_auth_pref_manager.h
+++ b/chromeos/components/proximity_auth/proximity_auth_pref_manager.h
@@ -64,10 +64,14 @@
   virtual void SetProximityThreshold(ProximityThreshold value) = 0;
   virtual ProximityThreshold GetProximityThreshold() const = 0;
 
-  // Setting and getter for whether EasyUnlock is enabled for ChromeOS login (in
+  // Getter for whether EasyUnlock is allowed for ChromeOS login (in addition to
+  // screen lock).
+  virtual bool IsChromeOSLoginAllowed() const = 0;
+
+  // Setter and getter for whether EasyUnlock is enabled for ChromeOS login (in
   // addition to screen lock).
   virtual void SetIsChromeOSLoginEnabled(bool is_enabled) = 0;
-  virtual bool IsChromeOSLoginEnabled() = 0;
+  virtual bool IsChromeOSLoginEnabled() const = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ProximityAuthPrefManager);
diff --git a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index 9bf657bb..09c9521 100644
--- a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -85,6 +85,8 @@
                  on_pref_changed_callback);
   registrar_.Add(proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled,
                  on_pref_changed_callback);
+  registrar_.Add(chromeos::multidevice_setup::kSmartLockSigninAllowedPrefName,
+                 on_pref_changed_callback);
 
   SyncPrefsToLocalState();
 }
@@ -103,6 +105,9 @@
                           base::Value(GetProximityThreshold()));
   user_prefs_dict->SetKey(prefs::kProximityAuthIsChromeOSLoginEnabled,
                           base::Value(IsChromeOSLoginEnabled()));
+  user_prefs_dict->SetKey(
+      chromeos::multidevice_setup::kSmartLockSigninAllowedPrefName,
+      base::Value(IsChromeOSLoginAllowed()));
 
   DictionaryPrefUpdate update(local_state_,
                               prefs::kEasyUnlockLocalStateUserPrefs);
@@ -173,13 +178,18 @@
   return static_cast<ProximityThreshold>(pref_value);
 }
 
+bool ProximityAuthProfilePrefManager::IsChromeOSLoginAllowed() const {
+  return pref_service_->GetBoolean(
+      chromeos::multidevice_setup::kSmartLockSigninAllowedPrefName);
+}
+
 void ProximityAuthProfilePrefManager::SetIsChromeOSLoginEnabled(
     bool is_enabled) {
   return pref_service_->SetBoolean(prefs::kProximityAuthIsChromeOSLoginEnabled,
                                    is_enabled);
 }
 
-bool ProximityAuthProfilePrefManager::IsChromeOSLoginEnabled() {
+bool ProximityAuthProfilePrefManager::IsChromeOSLoginEnabled() const {
   return pref_service_->GetBoolean(prefs::kProximityAuthIsChromeOSLoginEnabled);
 }
 
diff --git a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.h b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.h
index 201c13b..8327942 100644
--- a/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.h
+++ b/chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.h
@@ -64,8 +64,9 @@
   int GetPromotionShownCount() const override;
   void SetProximityThreshold(ProximityThreshold value) override;
   ProximityThreshold GetProximityThreshold() const override;
+  bool IsChromeOSLoginAllowed() const override;
   void SetIsChromeOSLoginEnabled(bool is_enabled) override;
-  bool IsChromeOSLoginEnabled() override;
+  bool IsChromeOSLoginEnabled() const override;
 
   // chromeos::multidevice_setup::MultiDeviceSetupClient::Observer:
   void OnFeatureStatesChanged(
diff --git a/chromeos/network/network_cert_loader.cc b/chromeos/network/network_cert_loader.cc
index 24173ed..9fbfd09 100644
--- a/chromeos/network/network_cert_loader.cc
+++ b/chromeos/network/network_cert_loader.cc
@@ -235,12 +235,10 @@
 }
 
 NetworkCertLoader::NetworkCertLoader() : weak_factory_(this) {
-  system_cert_cache_ = std::make_unique<CertCache>(
-      base::BindRepeating(&NetworkCertLoader::OnCertCacheOrPolicyCertsUpdated,
-                          base::Unretained(this)));
-  user_cert_cache_ = std::make_unique<CertCache>(
-      base::BindRepeating(&NetworkCertLoader::OnCertCacheOrPolicyCertsUpdated,
-                          base::Unretained(this)));
+  system_cert_cache_ = std::make_unique<CertCache>(base::BindRepeating(
+      &NetworkCertLoader::OnCertCacheUpdated, base::Unretained(this)));
+  user_cert_cache_ = std::make_unique<CertCache>(base::BindRepeating(
+      &NetworkCertLoader::OnCertCacheUpdated, base::Unretained(this)));
 }
 
 NetworkCertLoader::~NetworkCertLoader() {
@@ -260,7 +258,7 @@
     PolicyCertificateProvider* policy_certificate_provider) {
   policy_certificate_provider->AddPolicyProvidedCertsObserver(this);
   policy_certificate_providers_.push_back(policy_certificate_provider);
-  OnCertCacheOrPolicyCertsUpdated();
+  UpdateCertificates();
 }
 
 void NetworkCertLoader::RemovePolicyCertificateProvider(
@@ -271,7 +269,7 @@
   DCHECK(iter != policy_certificate_providers_.end());
   policy_certificate_providers_.erase(iter);
   policy_certificate_provider->RemovePolicyProvidedCertsObserver(this);
-  OnCertCacheOrPolicyCertsUpdated();
+  UpdateCertificates();
 }
 
 void NetworkCertLoader::AddObserver(NetworkCertLoader::Observer* observer) {
@@ -340,26 +338,9 @@
   return pkcs11_id;
 }
 
-void NetworkCertLoader::OnCertCacheOrPolicyCertsUpdated() {
+void NetworkCertLoader::OnCertCacheUpdated() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  VLOG(1) << "OnCertCacheOrPolicyCertsUpdated";
-
-  // Only trigger a notification to observers if one of the |CertCache|s has
-  // already loaded certificates. Don't trigger notifications if policy-provided
-  // certificates change before that.
-  // TODO(https://crbug.com/888451): When we handle client and authority
-  // certificates separately in NetworkCertLoader, we could fire different
-  // notifications for policy-provided cert changes instead of holding back
-  // notifications. Note that it is possible that only |system_cert_cache_| has
-  // loaded certificates (e.g. on the ChromeOS sign-in screen), and it is also
-  // possible that only |user_cert_cache_| has loaded certificates (e.g. if the
-  // system slot is not available for some reason, but a primary user has signed
-  // in).
-  bool has_loaded_any_certs_from_db =
-      user_cert_cache_->initial_load_finished() ||
-      system_cert_cache_->initial_load_finished();
-  if (!has_loaded_any_certs_from_db)
-    return;
+  VLOG(1) << "OnCertCacheUpdated";
 
   // If user_cert_cache_ has access to system certificates and it has already
   // finished its initial load, it will contain system certificates which we can
@@ -376,7 +357,7 @@
                        net::x509_util::DupCERTCertificateList(
                            user_cert_cache_->cert_list()),
                        std::move(system_slot)),
-        base::BindOnce(&NetworkCertLoader::UpdateCertificates,
+        base::BindOnce(&NetworkCertLoader::StoreCertsFromCache,
                        weak_factory_.GetWeakPtr(),
                        net::x509_util::DupCERTCertificateList(
                            user_cert_cache_->cert_list())));
@@ -384,29 +365,61 @@
     // The user's cert cache does not contain system certificates.
     net::ScopedCERTCertificateList system_token_client_certs =
         net::x509_util::DupCERTCertificateList(system_cert_cache_->cert_list());
-    net::ScopedCERTCertificateList all_certs =
+    net::ScopedCERTCertificateList all_certs_from_cache =
         net::x509_util::DupCERTCertificateList(user_cert_cache_->cert_list());
-    all_certs.reserve(all_certs.size() + system_token_client_certs.size());
-    for (const net::ScopedCERTCertificate& cert : system_token_client_certs)
-      all_certs.push_back(net::x509_util::DupCERTCertificate(cert.get()));
-    UpdateCertificates(std::move(all_certs),
-                       std::move(system_token_client_certs));
+    all_certs_from_cache.reserve(all_certs_from_cache.size() +
+                                 system_token_client_certs.size());
+    for (const net::ScopedCERTCertificate& cert : system_token_client_certs) {
+      all_certs_from_cache.push_back(
+          net::x509_util::DupCERTCertificate(cert.get()));
+    }
+    StoreCertsFromCache(std::move(all_certs_from_cache),
+                        std::move(system_token_client_certs));
   }
 }
 
-void NetworkCertLoader::UpdateCertificates(
-    net::ScopedCERTCertificateList all_certs,
+void NetworkCertLoader::StoreCertsFromCache(
+    net::ScopedCERTCertificateList all_certs_from_cache,
     net::ScopedCERTCertificateList system_token_client_certs) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  VLOG(1) << "UpdateCertificates: " << all_certs.size() << " ("
+  VLOG(1) << "StoreCertsFromCache: " << all_certs_from_cache.size() << " ("
           << system_token_client_certs.size()
           << " client certs on system slot)";
 
   // Ignore any existing certificates.
-  all_certs_ = std::move(all_certs);
+  all_certs_from_cache_ = std::move(all_certs_from_cache);
   system_token_client_certs_ = std::move(system_token_client_certs);
 
+  certs_from_cache_loaded_ = true;
+
+  UpdateCertificates();
+}
+
+void NetworkCertLoader::UpdateCertificates() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // Only trigger a notification to observers if one of the |CertCache|s has
+  // already loaded certificates. Don't trigger notifications if policy-provided
+  // certificates change before that.
+  // TODO(https://crbug.com/888451): When we handle client and authority
+  // certificates separately in NetworkCertLoader, we could fire different
+  // notifications for policy-provided cert changes instead of holding back
+  // notifications. Note that it is possible that only |system_cert_cache_| has
+  // loaded certificates (e.g. on the ChromeOS sign-in screen), and it is also
+  // possible that only |user_cert_cache_| has loaded certificates (e.g. if the
+  // system slot is not available for some reason, but a primary user has signed
+  // in).
+  if (!certs_from_cache_loaded_)
+    return;
+
+  // Copy |all_certs_from_cache_| into |all_certs_|, ignoring any existing
+  // certificates.
+  all_certs_.clear();
+  all_certs_.reserve(all_certs_from_cache_.size());
+  for (const net::ScopedCERTCertificate& cert : all_certs_from_cache_)
+    all_certs_.push_back(net::x509_util::DupCERTCertificate(cert.get()));
+
   // Add policy-provided certificates.
   // TODO(https://crbug.com/888451): Instead of putting authorities and client
   // certs into |all_certs_| and then filtering in NetworkCertificateHandler, we
@@ -429,7 +442,7 @@
     const net::CertificateList& all_server_and_authority_certs,
     const net::CertificateList& trust_anchors) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  OnCertCacheOrPolicyCertsUpdated();
+  UpdateCertificates();
 }
 
 }  // namespace chromeos
diff --git a/chromeos/network/network_cert_loader.h b/chromeos/network/network_cert_loader.h
index 7e77545..05fa679 100644
--- a/chromeos/network/network_cert_loader.h
+++ b/chromeos/network/network_cert_loader.h
@@ -150,15 +150,21 @@
   NetworkCertLoader();
   ~NetworkCertLoader() override;
 
-  // Called when |system_cert_cache_|, |user_cert_cache| or policy-provided
-  // certificates have potentially changed.
-  void OnCertCacheOrPolicyCertsUpdated();
+  // Called when |system_cert_cache_| or |user_cert_cache| certificates have
+  // potentially changed.
+  void OnCertCacheUpdated();
 
-  // Called if a certificate load task is finished.
-  void UpdateCertificates(
+  // Called as a result of |OnCertCacheUpdated|. This is a separate function,
+  // because |OnCertCacheUpdated| may trigger a background task for filtering
+  // certificates.
+  void StoreCertsFromCache(
       net::ScopedCERTCertificateList all_certs,
       net::ScopedCERTCertificateList system_token_client_certs);
 
+  // Called when policy-provided certificates or cache-based certificates (see
+  // |all_certs_from_cache_|) have potentially changed.
+  void UpdateCertificates();
+
   void NotifyCertificatesLoaded();
 
   // PolicyCertificateProvider::Observer
@@ -177,9 +183,15 @@
   // certificates.
   net::ScopedCERTCertificateList all_certs_;
 
+  // Cached certificates loaded from the database(s).
+  net::ScopedCERTCertificateList all_certs_from_cache_;
+
   // Cached certificates from system token.
   net::ScopedCERTCertificateList system_token_client_certs_;
 
+  // True if |StoreCertsFromCache()| was called before.
+  bool certs_from_cache_loaded_ = false;
+
   std::vector<const PolicyCertificateProvider*> policy_certificate_providers_;
 
   THREAD_CHECKER(thread_checker_);
diff --git a/chromeos/services/device_sync/device_sync_base.cc b/chromeos/services/device_sync/device_sync_base.cc
index 57c95fd2..fc4db5c 100644
--- a/chromeos/services/device_sync/device_sync_base.cc
+++ b/chromeos/services/device_sync/device_sync_base.cc
@@ -4,13 +4,17 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "chromeos/services/device_sync/device_sync_base.h"
 
 namespace chromeos {
 
 namespace device_sync {
 
-DeviceSyncBase::DeviceSyncBase() = default;
+DeviceSyncBase::DeviceSyncBase() {
+  bindings_.set_connection_error_handler(base::BindRepeating(
+      &DeviceSyncBase::OnDisconnection, base::Unretained(this)));
+}
 
 DeviceSyncBase::~DeviceSyncBase() = default;
 
@@ -33,6 +37,12 @@
   observers_.ForAllPtrs([](auto* observer) { observer->OnNewDevicesSynced(); });
 }
 
+void DeviceSyncBase::OnDisconnection() {
+  // If all clients have disconnected, shut down.
+  if (bindings_.empty())
+    Shutdown();
+}
+
 }  // namespace device_sync
 
 }  // namespace chromeos
diff --git a/chromeos/services/device_sync/device_sync_base.h b/chromeos/services/device_sync/device_sync_base.h
index 617b477..67346d5 100644
--- a/chromeos/services/device_sync/device_sync_base.h
+++ b/chromeos/services/device_sync/device_sync_base.h
@@ -31,10 +31,16 @@
  protected:
   DeviceSyncBase();
 
+  // Derived types should override this function to remove references to any
+  // dependencies.
+  virtual void Shutdown() {}
+
   void NotifyOnEnrollmentFinished();
   void NotifyOnNewDevicesSynced();
 
  private:
+  void OnDisconnection();
+
   mojo::InterfacePtrSet<mojom::DeviceSyncObserver> observers_;
   mojo::BindingSet<mojom::DeviceSync> bindings_;
 
@@ -45,4 +51,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_BASE_H_
\ No newline at end of file
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_BASE_H_
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index be5df26..a2eed36 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -262,6 +262,23 @@
   NotifyOnNewDevicesSynced();
 }
 
+void DeviceSyncImpl::Shutdown() {
+  software_feature_manager_.reset();
+  remote_device_provider_.reset();
+  cryptauth_device_manager_.reset();
+  cryptauth_enrollment_manager_.reset();
+  cryptauth_client_factory_.reset();
+  cryptauth_gcm_manager_.reset();
+  pref_connection_delegate_.reset();
+
+  identity_manager_ = nullptr;
+  gcm_driver_ = nullptr;
+  connector_ = nullptr;
+  gcm_device_info_provider_ = nullptr;
+  url_loader_factory_ = nullptr;
+  clock_ = nullptr;
+}
+
 void DeviceSyncImpl::ProcessPrimaryAccountInfo(
     const AccountInfo& primary_account_info) {
   // Note: We cannot use |primary_account_info.IsValid()| here because
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index 85ca389e..e5b3f8d 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -141,6 +141,9 @@
       base::Clock* clock,
       std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate);
 
+  // DeviceSyncBase:
+  void Shutdown() override;
+
   void ProcessPrimaryAccountInfo(const AccountInfo& primary_account_info);
   void ConnectToPrefStore();
   void OnConnectedToPrefService(std::unique_ptr<PrefService> pref_service);
diff --git a/components/arc/common/input_method_manager.mojom b/components/arc/common/input_method_manager.mojom
index 1585efc..c8d18975 100644
--- a/components/arc/common/input_method_manager.mojom
+++ b/components/arc/common/input_method_manager.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: 4
+// Next MinVersion: 5
 
 module arc.mojom;
 
@@ -104,7 +104,7 @@
 
 // This interface provides methods to control Android's InputMethodManager.
 //
-// Next method ID: 5
+// Next method ID: 7
 interface InputMethodManagerInstance {
   // Establishes full-duplex communication with the host.
   Init@0(InputMethodManagerHost host_ptr) => ();
@@ -123,4 +123,10 @@
 
   // Sends the latest TextInputState of the active text field.
   [MinVersion=2] UpdateTextInputState@4(TextInputState state);
+
+  // Requests the active IME to show virtual keyboard.
+  [MinVersion=4] ShowVirtualKeyboard@5();
+
+  // Requests the active IME to hide virtual keyboard.
+  [MinVersion=4] HideVirtualKeyboard@6();
 };
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index a1948e6..5359fc93 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -147,6 +147,8 @@
     "phone_number_i18n.h",
     "popup_item_ids.h",
     "popup_types.h",
+    "randomized_encoder.cc",
+    "randomized_encoder.h",
     "rationalization_util.cc",
     "rationalization_util.h",
     "region_combobox_model.cc",
@@ -278,6 +280,7 @@
     "//components/variations/net",
     "//components/version_info",
     "//components/webdata/common",
+    "//crypto",
     "//google_apis",
     "//net",
     "//services/identity/public/cpp",
@@ -490,6 +493,7 @@
     "phone_field_unittest.cc",
     "phone_number_i18n_unittest.cc",
     "phone_number_unittest.cc",
+    "randomized_encoder_unittest.cc",
     "rationalization_util_unittest.cc",
     "region_combobox_model_unittest.cc",
     "search_field_unittest.cc",
diff --git a/components/autofill/core/browser/DEPS b/components/autofill/core/browser/DEPS
index fb72964..889d643 100644
--- a/components/autofill/core/browser/DEPS
+++ b/components/autofill/core/browser/DEPS
@@ -14,6 +14,7 @@
   "+components/version_info",
   "+components/webdata/common",
   "+components/webdata_services",
+  "+crypto/hkdf.h",
   "+crypto/random.h",
   "+google_apis/gaia",
   "+google_apis/google_api_keys.h",
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 246ac0ef..b1e466e 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -2907,6 +2907,9 @@
   // Verify only the first complete number is filled when there are multiple
   // componentized number fields.
   FormData form_with_multiple_componentized_phone_fields;
+  form_with_multiple_componentized_phone_fields.origin =
+      GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3004,6 +3007,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_multiple_whole_number_fields;
+  form_with_multiple_whole_number_fields.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3085,6 +3090,9 @@
   // Verify only the first complete number is filled when there are multiple
   // componentized number fields.
   FormData form_with_multiple_componentized_phone_fields;
+  form_with_multiple_componentized_phone_fields.origin =
+      GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3188,6 +3196,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_misclassified_extension;
+  form_with_misclassified_extension.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3278,6 +3288,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_no_complete_number;
+  form_with_no_complete_number.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3363,6 +3375,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_multiple_whole_number_fields;
+  form_with_multiple_whole_number_fields.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3448,6 +3462,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_multiple_whole_number_fields;
+  form_with_multiple_whole_number_fields.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3601,6 +3617,8 @@
   std::string guid(work_profile->guid());
 
   FormData form_with_multiple_sections;
+  form_with_multiple_sections.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   // Default is zero, have to set to a number autofill can process.
   field.max_length = 10;
@@ -3800,6 +3818,8 @@
 TEST_F(AutofillManagerTest, FormChangesVisibilityOfFields) {
   // Set up our form data.
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
 
   // Default is zero, have to set to a number autofill can process.
@@ -4238,6 +4258,7 @@
 TEST_F(AutofillManagerTest, DetermineHeuristicsWithOverallPrediction) {
   // Set up our form data.
   FormData form;
+  form.origin = GURL("https://www.myform.com");
   FormFieldData field;
   test::CreateTestFormField("First Name", "firstname", "", "text", &field);
   form.fields.push_back(field);
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 0307e65..03540a6e 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -3173,6 +3173,7 @@
   // Set up the form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   std::vector<ServerFieldType> field_types;
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index f6de5a3..b241ed5 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -278,6 +278,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -316,6 +318,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_BadEmail) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -344,6 +348,8 @@
 // Tests that a 'confirm email' field does not block profile import.
 TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoEmails) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name:", "name", "George Washington", "text",
                             &field);
@@ -375,6 +381,8 @@
 // Tests two email fields containing different values blocks profile import.
 TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoDifferentEmails) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name:", "name", "George Washington", "text",
                             &field);
@@ -404,6 +412,8 @@
 // Tests that not enough filled fields will result in not importing an address.
 TEST_F(FormDataImporterTest, ImportAddressProfiles_NotEnoughFilledFields) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -426,6 +436,8 @@
   // United States addresses must specifiy one address line, a city, state and
   // zip code.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name:", "name", "Barack Obama", "text", &field);
   form.fields.push_back(field);
@@ -453,6 +465,8 @@
   // British addresses do not require a state/province as the county is usually
   // not requested on forms.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name:", "name", "David Cameron", "text", &field);
   form.fields.push_back(field);
@@ -480,6 +494,8 @@
   // Gibraltar has the most minimal set of requirements for a valid address.
   // There are no cities or provinces and no postal/zip code system.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name:", "name", "Sir Adrian Johns", "text",
                             &field);
@@ -501,6 +517,8 @@
 TEST_F(FormDataImporterTest,
        ImportAddressProfiles_PhoneNumberSplitAcrossMultipleFields) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -547,6 +565,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_MultilineAddress) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -588,6 +608,8 @@
 TEST_F(FormDataImporterTest,
        ImportAddressProfiles_TwoValidProfilesDifferentForms) {
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -626,6 +648,8 @@
 
   // Now create a completely different profile.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   test::CreateTestFormField("First name:", "first_name", "John", "text",
                             &field);
   form2.fields.push_back(field);
@@ -662,6 +686,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoValidProfilesSameForm) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -730,6 +756,8 @@
 TEST_F(FormDataImporterTest,
        ImportAddressProfiles_OneValidProfileSameForm_PartsHidden) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -799,6 +827,8 @@
 // A maximum of two address profiles are imported per form.
 TEST_F(FormDataImporterTest, ImportAddressProfiles_ThreeValidProfilesSameForm) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -886,6 +916,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) {
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -929,6 +961,8 @@
 
   // Now create an updated profile.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
   form2.fields.push_back(field);
@@ -977,6 +1011,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) {
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1011,6 +1047,8 @@
 
   // Submit a form with new data for the first profile.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
   form2.fields.push_back(field);
@@ -1051,6 +1089,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) {
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1092,6 +1132,8 @@
 
   // Submit a form with new data for the first profile.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
   form2.fields.push_back(field);
@@ -1129,6 +1171,8 @@
 
 TEST_F(FormDataImporterTest, ImportAddressProfiles_InsufficientAddress) {
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1180,6 +1224,8 @@
 
   // Simulate a form submission with conflicting info.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "Marion Mitchell",
                             "text", &field);
@@ -1238,6 +1284,8 @@
 // Tests that no profile is inferred if the country is not recognized.
 TEST_F(FormDataImporterTest, ImportAddressProfiles_UnrecognizedCountry) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1277,6 +1325,8 @@
 TEST_F(FormDataImporterTest,
        ImportAddressProfiles_CompleteComposedCountryName) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1323,6 +1373,8 @@
 TEST_F(FormDataImporterTest,
        ImportAddressProfiles_IncompleteComposedCountryName) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -1365,6 +1417,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_Valid) {
   // Add a single valid credit card form.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2999");
 
@@ -1393,6 +1447,8 @@
 // Tests that an invalid credit card number is not extracted.
 TEST_F(FormDataImporterTest, ImportCreditCard_InvalidCardNumber) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Jim Johansen", "1000000000000000", "02",
                         "2999");
 
@@ -1416,6 +1472,8 @@
 // Tests that an invalid credit card expiration is not extracted.
 TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Smalls Biggie", "4111-1111-1111-1111", "0",
                         "2999");
 
@@ -1440,6 +1498,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_MonthSelectInvalidText) {
   // Add a single valid credit card form with an invalid option value.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111",
                         "Feb (2)", "2999");
   // Add option values and contents to the expiration month field.
@@ -1481,6 +1541,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_TwoValidCards) {
   // Start with a single valid credit card form.
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2999");
 
@@ -1503,6 +1565,8 @@
 
   // Add a second different valid credit card.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, "", "5500 0000 0000 0004", "02", "2999");
 
   FormStructure form_structure2(form2);
@@ -1527,6 +1591,8 @@
 // This form has the expiration year as one field with MM/YY.
 TEST_F(FormDataImporterTest, ImportCreditCard_Month2DigitYearCombination) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "John MMYY",
                             "text", &field);
@@ -1546,6 +1612,8 @@
 // This form has the expiration year as one field with MM/YYYY.
 TEST_F(FormDataImporterTest, ImportCreditCard_Month4DigitYearCombination) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "John MMYYYY",
                             "text", &field);
@@ -1565,6 +1633,8 @@
 // This form has the expiration year as one field with M/YYYY.
 TEST_F(FormDataImporterTest, ImportCreditCard_1DigitMonth4DigitYear) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "John MYYYY",
                             "text", &field);
@@ -1583,6 +1653,8 @@
 // This form has the expiration year as a 2-digit field.
 TEST_F(FormDataImporterTest, ImportCreditCard_2DigitYear) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "John Smith",
                             "text", &field);
@@ -1619,6 +1691,8 @@
 
   // Type the same data as the masked card into a form.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "John Dillinger", "4111111111111111", "01",
                         "2999");
 
@@ -1649,6 +1723,8 @@
 
   // Type the same data as the unmasked card into a form.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Clyde Barrow", "378282246310005", "04", "2999");
 
   // The card should not be offered to be saved locally because it only matches
@@ -1663,6 +1739,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_SameCreditCardWithConflict) {
   // Start with a single valid credit card form.
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2998");
 
@@ -1686,6 +1764,8 @@
   // Add a second different valid credit card where the year is different but
   // the credit card number matches.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         /* different year */ "2999");
 
@@ -1711,6 +1791,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_ShouldReturnLocalCard) {
   // Start with a single valid credit card form.
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2998");
 
@@ -1734,6 +1816,8 @@
   // Add a second different valid credit card where the year is different but
   // the credit card number matches.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         /* different year */ "2999");
 
@@ -1762,6 +1846,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_EmptyCardWithConflict) {
   // Start with a single valid credit card form.
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2998");
 
@@ -1785,6 +1871,8 @@
 
   // Add a second credit card with no number.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, "Biggie Smalls", /* no number */ nullptr, "01",
                         "2999");
 
@@ -1812,6 +1900,8 @@
 TEST_F(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) {
   // Start with a single valid credit card form.
   FormData form1;
+  form1.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2999");
 
@@ -1835,6 +1925,8 @@
   // Add a second different valid credit card where the name is missing but
   // the credit card number matches.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, /* missing name */ nullptr,
                         "4111-1111-1111-1111", "01", "2999");
 
@@ -1859,6 +1951,8 @@
 
   // Add a third credit card where the expiration date is missing.
   FormData form3;
+  form3.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form3, "Johnny McEnroe", "5555555555554444",
                         /* no month */ nullptr,
                         /* no year */ nullptr);
@@ -1902,6 +1996,8 @@
   // Add a second different valid credit card where the year is different but
   // the credit card number matches.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         /* different year */ "2999");
 
@@ -1943,6 +2039,8 @@
 
   // Import the same card info, but with different separators in the number.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01",
                         "2999");
 
@@ -1983,6 +2081,8 @@
 
   // Simulate a form submission with conflicting expiration year.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         /* different year */ "2999");
 
@@ -2021,6 +2121,8 @@
 
   // Simulate a form submission with the same card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         "2999");
 
@@ -2041,6 +2143,8 @@
   // |imported_credit_card_record_type_| should be reset.
   // Simulate a form submission with a new card.
   FormData form2;
+  form2.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form2, "Biggie Smalls", "4012888888881881", "01",
                         "2999");
 
@@ -2062,6 +2166,8 @@
   // |imported_credit_card_record_type_| should still be reset even if
   // ImportCreditCard is not called. Simulate a form submission with no card.
   FormData form3;
+  form3.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -2099,6 +2205,8 @@
        ImportFormData_ImportCreditCardRecordType_NewCard) {
   // Simulate a form submission with a new credit card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         "2999");
 
@@ -2134,6 +2242,8 @@
 
   // Simulate a form submission with the same card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         "2999");
 
@@ -2169,6 +2279,8 @@
 
   // Simulate a form submission with the same masked server card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         "2999");
 
@@ -2203,6 +2315,8 @@
 
   // Simulate a form submission with the same full server card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "378282246310005", "04",
                         "2999");
 
@@ -2224,6 +2338,8 @@
        ImportFormData_ImportCreditCardRecordType_NoCard_InvalidCardNumber) {
   // Simulate a form submission using a credit card with an invalid card number.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1112", "01",
                         "2999");
 
@@ -2246,6 +2362,8 @@
        ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard) {
   // Simulate a form submission with an expired credit card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
                         "1999");
 
@@ -2268,6 +2386,8 @@
        ImportFormData_ImportCreditCardRecordType_NoCard_NoCardOnForm) {
   // Simulate a form submission with no credit card on form.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("First name:", "first_name", "George", "text",
                             &field);
@@ -2308,6 +2428,8 @@
 // address and the credit card.
 TEST_F(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   // Address section.
   test::CreateTestFormField("First name:", "first_name", "George", "text",
@@ -2371,6 +2493,8 @@
 // import the address but does import the credit card.
 TEST_F(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   // Address section 1.
   test::CreateTestFormField("First name:", "first_name", "George", "text",
@@ -2441,6 +2565,8 @@
 // the credit card if addresses are disabled.
 TEST_F(FormDataImporterTest, ImportFormData_AddressesDisabledOneCreditCard) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   // Address section.
   test::CreateTestFormField("First name:", "first_name", "George", "text",
@@ -2495,6 +2621,8 @@
 // the address if credit cards are disabled.
 TEST_F(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   // Address section.
   test::CreateTestFormField("First name:", "first_name", "George", "text",
@@ -2553,6 +2681,8 @@
 // if both addressed and credit cards are disabled.
 TEST_F(FormDataImporterTest, ImportFormData_AddressCreditCardDisabled) {
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   // Address section.
   test::CreateTestFormField("First name:", "first_name", "George", "text",
@@ -2621,6 +2751,8 @@
   // A valid credit card form. A user re-enters one of their masked cards.
   // We should not offer to save locally.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "John Dillinger",
                             "text", &field);
@@ -2668,6 +2800,8 @@
   // here, either. Since it's unmasked, we know for certain that it's the same
   // card.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
                             "text", &field);
@@ -2710,6 +2844,8 @@
   // A user fills/enters the card's information on a checkout form.  Ensure that
   // an expiration date match is recorded.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
                             "text", &field);
@@ -2757,6 +2893,8 @@
   // the expiration date of the card.  Ensure that an expiration date mismatch
   // is recorded.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
                             "text", &field);
@@ -2804,6 +2942,8 @@
   // A user fills/enters the card's information on a checkout form.  Ensure that
   // an expiration date match is recorded.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
                             "text", &field);
@@ -2852,6 +2992,8 @@
   // the expiration date of the card.  Ensure that an expiration date mismatch
   // is recorded.
   FormData form;
+  form.origin = GURL("https://wwww.foo.com");
+
   FormFieldData field;
   test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
                             "text", &field);
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index bcf37e6..472cdfe 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -61,6 +61,14 @@
 const int kCommonNamePrefixRemovalFieldThreshold = 3;
 const int kMinCommonNamePrefixLength = 16;
 
+// Returns true if the scheme given by |url| is one for which autfill is allowed
+// to activate. By default this only returns true for HTTP and HTTPS.
+bool HasAllowedScheme(const GURL& url) {
+  return url.SchemeIsHTTPOrHTTPS() ||
+         base::FeatureList::IsEnabled(
+             features::kAutofillAllowNonHttpActivation);
+}
+
 // Helper for |EncodeUploadRequest()| that creates a bit field corresponding to
 // |available_field_types| and returns the hex representation as a string.
 std::string EncodeFieldTypes(const ServerFieldTypeSet& available_field_types) {
@@ -690,6 +698,10 @@
 }
 
 bool FormStructure::ShouldBeParsed() const {
+  // Exclude URLs not on the web via HTTP(S).
+  if (!HasAllowedScheme(source_url_))
+    return false;
+
   size_t min_required_fields =
       std::min({MinRequiredFieldsForHeuristics(), MinRequiredFieldsForQuery(),
                 MinRequiredFieldsForUpload()});
@@ -718,6 +730,7 @@
 
 bool FormStructure::ShouldRunHeuristics() const {
   return active_field_count() >= MinRequiredFieldsForHeuristics() &&
+         HasAllowedScheme(source_url_) &&
          (is_form_tag_ || is_formless_checkout_ ||
           !base::FeatureList::IsEnabled(
               features::kAutofillRestrictUnownedFieldsToFormlessCheckout));
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index d9eef3c0..0f881919 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -144,6 +144,7 @@
 TEST_F(FormStructureTest, FieldCount) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.label = ASCIIToUTF16("username");
@@ -169,13 +170,14 @@
 
   // The render process sends all fields to browser including fields with
   // autocomplete=off
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   EXPECT_EQ(4U, form_structure->field_count());
 }
 
 TEST_F(FormStructureTest, AutofillCount) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.label = ASCIIToUTF16("username");
@@ -209,7 +211,7 @@
   form.fields.push_back(field);
 
   // Only text and select fields that are heuristically matched are counted.
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_EQ(3U, form_structure->autofill_count());
 
@@ -221,7 +223,7 @@
   field.should_autocomplete = false;
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_EQ(4U, form_structure->autofill_count());
 }
@@ -236,6 +238,7 @@
 
 TEST_F(FormStructureTest, IsAutofillable) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
   FormFieldData field;
 
   // Start with a username field. It should be picked up by the password but
@@ -303,6 +306,7 @@
 
 TEST_F(FormStructureTest, ShouldBeParsed) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   // Start with a single checkable field.
   FormFieldData checkable_field;
@@ -396,11 +400,90 @@
   CheckFormShouldBeParsed("new password", form, true, true);
 }
 
+TEST_F(FormStructureTest, ShouldBeParsed_BadScheme) {
+  std::unique_ptr<FormStructure> form_structure;
+  FormData form;
+  FormFieldData field;
+
+  field.label = ASCIIToUTF16("Name");
+  field.name = ASCIIToUTF16("name");
+  field.form_control_type = "text";
+  field.autocomplete_attribute = "name";
+  form.fields.push_back(field);
+
+  field.label = ASCIIToUTF16("Email");
+  field.name = ASCIIToUTF16("email");
+  field.form_control_type = "text";
+  field.autocomplete_attribute = "email";
+  form.fields.push_back(field);
+
+  field.label = ASCIIToUTF16("Address");
+  field.name = ASCIIToUTF16("address");
+  field.form_control_type = "text";
+  field.autocomplete_attribute = "address-line1";
+  form.fields.push_back(field);
+
+  // Baseline, HTTP should work.
+  form.origin = GURL("http://wwww.foo.com/myform");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_TRUE(form_structure->ShouldBeParsed());
+  EXPECT_TRUE(form_structure->ShouldRunHeuristics());
+  EXPECT_TRUE(form_structure->ShouldBeQueried());
+  EXPECT_TRUE(form_structure->ShouldBeUploaded());
+
+  // Baseline, HTTPS should work.
+  form.origin = GURL("https://wwww.foo.com/myform");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_TRUE(form_structure->ShouldBeParsed());
+  EXPECT_TRUE(form_structure->ShouldRunHeuristics());
+  EXPECT_TRUE(form_structure->ShouldBeQueried());
+  EXPECT_TRUE(form_structure->ShouldBeUploaded());
+
+  // Chrome internal urls shouldn't be parsed.
+  form.origin = GURL("chrome://settings");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_FALSE(form_structure->ShouldBeParsed());
+  EXPECT_FALSE(form_structure->ShouldRunHeuristics());
+  EXPECT_FALSE(form_structure->ShouldBeQueried());
+  EXPECT_FALSE(form_structure->ShouldBeUploaded());
+
+  // FTP urls shouldn't be parsed.
+  form.origin = GURL("ftp://ftp.foo.com/form.html");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_FALSE(form_structure->ShouldBeParsed());
+  EXPECT_FALSE(form_structure->ShouldRunHeuristics());
+  EXPECT_FALSE(form_structure->ShouldBeQueried());
+  EXPECT_FALSE(form_structure->ShouldBeUploaded());
+
+  // Blob urls shouldn't be parsed.
+  form.origin = GURL("blob://blob.foo.com/form.html");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_FALSE(form_structure->ShouldBeParsed());
+  EXPECT_FALSE(form_structure->ShouldRunHeuristics());
+  EXPECT_FALSE(form_structure->ShouldBeQueried());
+  EXPECT_FALSE(form_structure->ShouldBeUploaded());
+
+  // About urls shouldn't be parsed.
+  form.origin = GURL("about://about.foo.com/form.html");
+  form_structure = std::make_unique<FormStructure>(form);
+  form_structure->ParseFieldTypesFromAutocompleteAttributes();
+  EXPECT_FALSE(form_structure->ShouldBeParsed());
+  EXPECT_FALSE(form_structure->ShouldRunHeuristics());
+  EXPECT_FALSE(form_structure->ShouldBeQueried());
+  EXPECT_FALSE(form_structure->ShouldBeUploaded());
+}
+
 // Tests that ShouldBeParsed returns true for a form containing less than three
 // fields if at least one has an autocomplete attribute.
 TEST_F(FormStructureTest, ShouldBeParsed_TwoFields_HasAutocomplete) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
   FormFieldData field;
 
   field.label = ASCIIToUTF16("Name");
@@ -415,7 +498,7 @@
   field.autocomplete_attribute = "";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->ParseFieldTypesFromAutocompleteAttributes();
   EXPECT_TRUE(form_structure->ShouldBeParsed());
 }
@@ -425,6 +508,7 @@
 TEST_F(FormStructureTest, DetermineHeuristicTypes_AutocompleteFalse) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
   FormFieldData field;
 
   field.label = ASCIIToUTF16("Name");
@@ -445,7 +529,7 @@
   field.autocomplete_attribute = "false";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->ShouldBeParsed());
   EXPECT_EQ(3U, form_structure->autofill_count());
@@ -458,6 +542,7 @@
 TEST_F(FormStructureTest, HeuristicsContactInfo) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -499,7 +584,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -532,6 +617,7 @@
 TEST_F(FormStructureTest, HeuristicsAutocompleteAttribute) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -556,7 +642,7 @@
   field.autocomplete_attribute = "upi-vpa";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->has_author_specified_types());
@@ -583,6 +669,7 @@
       features::kAutofillRestrictUnownedFieldsToFormlessCheckout);
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -602,7 +689,7 @@
   field.autocomplete_attribute = "email";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -618,7 +705,7 @@
   form.is_formless_checkout = false;
   form.is_form_tag = false;
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -635,6 +722,8 @@
 // that the common prefix is stripped out before running heuristics.
 TEST_F(FormStructureTest, StripCommonNamePrefix) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   field.form_control_type = "text";
 
@@ -684,6 +773,8 @@
 // stripped from the name before running the heuristics.
 TEST_F(FormStructureTest, StripCommonNamePrefix_SmallPrefix) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   field.form_control_type = "text";
 
@@ -718,6 +809,7 @@
 TEST_F(FormStructureTest, IsCompleteCreditCardForm_Minimal) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -735,7 +827,7 @@
   field.name = ASCIIToUTF16("zip");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
@@ -744,6 +836,7 @@
 TEST_F(FormStructureTest, IsCompleteCreditCardForm_Full) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -773,7 +866,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
@@ -783,6 +876,7 @@
 TEST_F(FormStructureTest, IsCompleteCreditCardForm_OnlyCCNumber) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -791,7 +885,7 @@
   field.name = ASCIIToUTF16("card_number");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
@@ -801,6 +895,7 @@
 TEST_F(FormStructureTest, IsCompleteCreditCardForm_AddressForm) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -832,7 +927,7 @@
   field.label = ASCIIToUTF16("Zip code");
   field.name = base::string16();
   form.fields.push_back(field);
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
@@ -843,6 +938,7 @@
 TEST_F(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -862,7 +958,7 @@
   field.autocomplete_attribute = "tel-local-suffix";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -886,6 +982,7 @@
        HeuristicsAndServerPredictions_BigForm_NoAutocompleteAttribute) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -902,7 +999,7 @@
   field.name = ASCIIToUTF16("email");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
@@ -922,6 +1019,7 @@
        HeuristicsAndServerPredictions_ValidAutocompleteAttribute) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -941,7 +1039,7 @@
   field.name = ASCIIToUTF16("email");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
@@ -962,6 +1060,7 @@
        HeuristicsAndServerPredictions_UnrecognizedAutocompleteAttribute) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -985,7 +1084,7 @@
   field.name = ASCIIToUTF16("email");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
@@ -1004,6 +1103,8 @@
 TEST_F(FormStructureTest,
        HeuristicsAndServerPredictions_SmallForm_NoAutocompleteAttribute) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   field.form_control_type = "text";
   field.label = ASCIIToUTF16("First Name");
@@ -1076,6 +1177,7 @@
 TEST_F(FormStructureTest,
        HeuristicsAndServerPredictions_SmallForm_ValidAutocompleteAttribute) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1162,6 +1264,7 @@
 // considered autofillable though.
 TEST_F(FormStructureTest, PasswordFormShouldBeQueried) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   // Start with a regular contact form.
   FormFieldData field;
@@ -1195,6 +1298,7 @@
 // attribute.
 TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1259,6 +1363,7 @@
 TEST_F(FormStructureTest,
        HeuristicsAutocompleteAttributeWithSectionsDegenerate) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1302,6 +1407,7 @@
 // |autocomplete| attribute.
 TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1331,6 +1437,7 @@
 // local heuristics.
 TEST_F(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1366,6 +1473,7 @@
 TEST_F(FormStructureTest, HeuristicsSample8) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1411,7 +1519,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(10U, form_structure->field_count());
@@ -1443,6 +1551,7 @@
 TEST_F(FormStructureTest, HeuristicsSample6) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1477,7 +1586,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(7U, form_structure->field_count());
@@ -1505,6 +1614,7 @@
 TEST_F(FormStructureTest, HeuristicsLabelsOnly) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1542,7 +1652,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(8U, form_structure->field_count());
@@ -1570,6 +1680,7 @@
 TEST_F(FormStructureTest, HeuristicsCreditCardInfo) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1599,7 +1710,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(6U, form_structure->field_count());
@@ -1624,6 +1735,7 @@
 TEST_F(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1659,7 +1771,7 @@
   field.form_control_type = "submit";
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(7U, form_structure->field_count());
@@ -1686,6 +1798,7 @@
 TEST_F(FormStructureTest, ThreeAddressLines) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1706,7 +1819,7 @@
   field.name = ASCIIToUTF16("city");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
@@ -1726,6 +1839,7 @@
 TEST_F(FormStructureTest, SurplusAddressLinesIgnored) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1746,7 +1860,7 @@
   field.name = ASCIIToUTF16("billing.address.addressLine4");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   ASSERT_EQ(4U, form_structure->field_count());
   ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1769,6 +1883,7 @@
 TEST_F(FormStructureTest, ThreeAddressLinesExpedia) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1789,7 +1904,7 @@
   field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adct");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
@@ -1811,6 +1926,7 @@
 TEST_F(FormStructureTest, TwoAddressLinesEbay) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1827,7 +1943,7 @@
   field.name = ASCIIToUTF16("city");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(3U, form_structure->field_count());
@@ -1844,6 +1960,7 @@
 TEST_F(FormStructureTest, HeuristicsStateWithProvince) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1860,7 +1977,7 @@
   field.name = ASCIIToUTF16("State");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(3U, form_structure->field_count());
@@ -1878,6 +1995,7 @@
 TEST_F(FormStructureTest, HeuristicsWithBilling) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1926,7 +2044,7 @@
   field.name = ASCIIToUTF16("email$emailBox");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(11U, form_structure->field_count());
@@ -1949,6 +2067,7 @@
 TEST_F(FormStructureTest, ThreePartPhoneNumber) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -1975,7 +2094,7 @@
   field.max_length = 0;
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
@@ -1994,6 +2113,7 @@
 TEST_F(FormStructureTest, HeuristicsInfernoCC) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2018,7 +2138,7 @@
   field.name = ASCIIToUTF16("expiration_year");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -2044,6 +2164,7 @@
 TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesNotFirst) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2072,7 +2193,7 @@
   field.name = ASCIIToUTF16("csc");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -2102,6 +2223,7 @@
 TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesFirst) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2130,7 +2252,7 @@
   field.name = ASCIIToUTF16("csc");
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
@@ -2156,6 +2278,7 @@
 
 TEST_F(FormStructureTest, EncodeQueryRequest) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2196,7 +2319,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(11337937696949187602U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 412125936U, "name_on_card",
                        "text");
@@ -2212,7 +2335,7 @@
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
 
-  const char kSignature1[] = "11337937696949187602";
+  const std::string kSignature1 = form_structure.FormSignatureAsStr();
 
   AutofillQueryContents encoded_query;
   ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures,
@@ -2250,7 +2373,7 @@
 
   // Add the second form to the expected proto.
   query_form = query.add_form();
-  query_form->set_signature(8308881815906226214U);
+  query_form->set_signature(form_structure3.form_signature());
 
   test::FillQueryField(query_form->add_field(), 412125936U, "name_on_card",
                        "text");
@@ -2274,7 +2397,7 @@
                                                 &encoded_query3));
   ASSERT_EQ(2U, encoded_signatures.size());
   EXPECT_EQ(kSignature1, encoded_signatures[0]);
-  const char kSignature2[] = "8308881815906226214";
+  const std::string kSignature2 = form_structure3.FormSignatureAsStr();
   EXPECT_EQ(kSignature2, encoded_signatures[1]);
 
   encoded_query3.SerializeToString(&encoded_query_string);
@@ -2317,7 +2440,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -2371,7 +2496,7 @@
       {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID});
   form.fields.push_back(checkable_field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2400,7 +2525,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(8736493185895608956U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("144200030e");
   upload.set_passwords_revealed(false);
@@ -2463,7 +2588,7 @@
          AutofillProfile::INVALID, AutofillProfile::INVALID});
   }
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2478,7 +2603,7 @@
   }
 
   // Adjust the expected proto string.
-  upload.set_form_signature(7816485729218079147U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   // Create an additional 2 fields (total of 7).  Put the appropriate autofill
   // type on the different address fields.
@@ -2508,7 +2633,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -2562,7 +2689,7 @@
       {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID});
   form.fields.push_back(checkable_field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2591,7 +2718,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(8736493185895608956U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("144200030e");
   upload.set_passwords_revealed(false);
@@ -2634,7 +2761,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -2689,7 +2818,7 @@
       {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID, AutofillProfile::VALID});
   form.fields.push_back(checkable_field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2718,7 +2847,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(8736493185895608956U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("144200030e");
   upload.set_passwords_revealed(false);
@@ -2759,7 +2888,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -2810,7 +2941,7 @@
                                              {ADDRESS_HOME_COUNTRY});
   form.fields.push_back(checkable_field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2842,7 +2973,7 @@
   upload.set_submission(true);
   upload.set_submission_event(AutofillUploadContents::HTML_FORM_SUBMISSION);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(8736493185895608956U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("144200030e");
   upload.set_passwords_revealed(false);
@@ -2895,7 +3026,7 @@
          ADDRESS_BILLING_LINE2});
   }
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_password_attributes_vote(
       std::make_pair(PasswordAttribute::kHasNumeric, true));
   form_structure->set_password_length_vote(10u);
@@ -2911,7 +3042,7 @@
   }
 
   // Adjust the expected proto string.
-  upload.set_form_signature(7816485729218079147U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_submission_event(
       AutofillUploadContents_SubmissionIndicatorEvent_HTML_FORM_SUBMISSION);
@@ -2947,7 +3078,7 @@
         {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1,
          ADDRESS_BILLING_LINE2});
   }
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
             possible_field_types_validities.size());
@@ -2968,7 +3099,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3004,7 +3137,7 @@
   test::InitializePossibleTypesAndValidities(possible_field_types,
                                              possible_field_types_validities,
                                              {ACCOUNT_CREATION_PASSWORD});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3038,7 +3171,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(5810032074788446513U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440000000000000000802");
   upload.set_action_signature(15724779818122431245U);
@@ -3090,7 +3223,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3115,7 +3250,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3136,7 +3271,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(14746822798145140279U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_action_signature(15724779818122431245U);
@@ -3170,7 +3305,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3206,7 +3343,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3227,7 +3364,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(14746822798145140279U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_passwords_revealed(false);
@@ -3263,7 +3400,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3285,7 +3424,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3306,7 +3445,7 @@
   AutofillUploadContents upload;
   upload.set_submission(false);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(14746822798145140279U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_action_signature(15724779818122431245U);
@@ -3339,7 +3478,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3357,7 +3498,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3378,7 +3519,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(6949133589768631292U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_action_signature(15724779818122431245U);
@@ -3409,6 +3550,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -3447,7 +3589,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(6949133589768631292U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_action_signature(15724779818122431245U);
@@ -3488,9 +3630,11 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   // Setting the form name which we expect to see in the upload.
   form.name = ASCIIToUTF16("myform");
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3506,7 +3650,7 @@
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_submission_source(SubmissionSource::FRAME_DETACHED);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
@@ -3528,7 +3672,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(2345951786066580868U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_action_signature(15724779818122431245U);
@@ -3561,7 +3705,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3585,7 +3731,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3606,7 +3752,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(13043654279838250996U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_passwords_revealed(false);
@@ -3641,7 +3787,9 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form_structure.reset(new FormStructure(form));
+  form.origin = GURL("http://www.foo.com/");
+
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
@@ -3672,7 +3820,7 @@
   form.fields.push_back(field);
   test::InitializePossibleTypesAndValidities(
       possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS});
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
   ASSERT_EQ(form_structure->field_count(),
@@ -3693,7 +3841,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(14746822798145140279U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(true);
   upload.set_data_present("1440");
   upload.set_passwords_revealed(false);
@@ -3723,6 +3871,7 @@
 // |available_types|.
 TEST_F(FormStructureTest, CheckDataPresence) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -3762,7 +3911,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(6402244543831589061U);
+  upload.set_form_signature(form_structure.form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("");
   upload.set_passwords_revealed(false);
@@ -3997,6 +4146,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -4026,7 +4176,7 @@
                                              possible_field_types_validities,
                                              {ADDRESS_HOME_LINE1});
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   form_structure->set_submission_source(SubmissionSource::XHR_SUCCEEDED);
   for (size_t i = 0; i < form_structure->field_count(); ++i) {
     form_structure->field(i)->set_possible_types(possible_field_types[i]);
@@ -4038,7 +4188,7 @@
   AutofillUploadContents upload;
   upload.set_submission(true);
   upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(18062476096658145866U);
+  upload.set_form_signature(form_structure->form_signature());
   upload.set_autofill_used(false);
   upload.set_data_present("1440000360000008");
   upload.set_passwords_revealed(false);
@@ -4133,6 +4283,8 @@
 
 TEST_F(FormStructureTest, EncodeUploadRequest_PasswordsRevealed) {
   FormData form;
+  form.origin = GURL("http://www.foo.com/");
+
   // Add 3 fields, to make the form uploadable.
   FormFieldData field;
   field.name = ASCIIToUTF16("email");
@@ -4175,25 +4327,25 @@
   field.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED;
   form.fields.push_back(field);
 
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
 
   EXPECT_EQ(FormStructureTest::Hash64Bit(std::string("://&&email&first")),
             form_structure->FormSignatureAsStr());
 
   form.origin = GURL(std::string("http://www.facebook.com"));
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   EXPECT_EQ(FormStructureTest::Hash64Bit(
                 std::string("http://www.facebook.com&&email&first")),
             form_structure->FormSignatureAsStr());
 
   form.action = GURL(std::string("https://login.facebook.com/path"));
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   EXPECT_EQ(FormStructureTest::Hash64Bit(
                 std::string("https://login.facebook.com&&email&first")),
             form_structure->FormSignatureAsStr());
 
   form.name = ASCIIToUTF16("login_form");
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   EXPECT_EQ(FormStructureTest::Hash64Bit(std::string(
                 "https://login.facebook.com&login_form&email&first")),
             form_structure->FormSignatureAsStr());
@@ -4213,7 +4365,7 @@
   field.label = ASCIIToUTF16("Random Field label3");
   field.name = ASCIIToUTF16("12345ran123456dom123");
   form.fields.push_back(field);
-  form_structure.reset(new FormStructure(form));
+  form_structure = std::make_unique<FormStructure>(form);
   EXPECT_EQ(FormStructureTest::Hash64Bit(
                 std::string("https://login.facebook.com&login_form&email&first&"
                             "random1234&random&1ran12dom&random123")),
@@ -4279,7 +4431,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(18006745212084723782U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 239111655U, "username", "text");
   test::FillQueryField(query_form->add_field(), 420638584U, "email", "text");
@@ -4331,7 +4483,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(13906559713264665730U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 239111655U, "username", "text");
   test::FillQueryField(query_form->add_field(), 420638584U, "email", "text");
@@ -4386,7 +4538,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(13906559713264665730U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 239111655U, "username", "text");
   test::FillQueryField(query_form->add_field(), 420638584U, "email", "text");
@@ -4435,7 +4587,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(16416961345885087496U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 239111655U, "username", "text");
   test::FillQueryField(query_form->add_field(), 1318412689U, nullptr, "text");
@@ -4486,7 +4638,7 @@
   AutofillQueryContents query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
   AutofillQueryContents::Form* query_form = query.add_form();
-  query_form->set_signature(7635954436925888745U);
+  query_form->set_signature(form_structure.form_signature());
 
   test::FillQueryField(query_form->add_field(), 239111655U, nullptr, nullptr);
   test::FillQueryField(query_form->add_field(), 3654076265U, nullptr, nullptr);
@@ -4508,6 +4660,8 @@
 
 TEST_F(FormStructureTest, PossibleValues) {
   FormData form_data;
+  form_data.origin = GURL("http://www.foo.com/");
+
   FormFieldData field;
   field.autocomplete_attribute = "billing country";
   field.option_contents.push_back(ASCIIToUTF16("Down Under"));
diff --git a/components/autofill/core/browser/randomized_encoder.cc b/components/autofill/core/browser/randomized_encoder.cc
new file mode 100644
index 0000000..13c9f58
--- /dev/null
+++ b/components/autofill/core/browser/randomized_encoder.cc
@@ -0,0 +1,210 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/randomized_encoder.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/format_macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "crypto/hkdf.h"
+
+namespace autofill {
+
+struct RandomizedEncoder::EncodingInfo {
+  AutofillRandomizedValue_EncodingType encoding_type;
+  size_t final_size;
+  size_t bit_offset;
+  size_t bit_stride;
+};
+
+namespace {
+
+const RandomizedEncoder::EncodingInfo kEncodingInfo[] = {
+    // One bit per byte. These all require 8 bytes to encode and have 8-bit
+    // strides, starting from a different initial bit offset.
+    {AutofillRandomizedValue_EncodingType_BIT_0, 8, 0, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_1, 8, 1, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_2, 8, 2, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_3, 8, 3, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_4, 8, 4, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_5, 8, 5, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_6, 8, 6, 8},
+    {AutofillRandomizedValue_EncodingType_BIT_7, 8, 7, 8},
+
+    // Four bits per byte. These require 32 bytes to encode and have 2-bit
+    // strides/
+    {AutofillRandomizedValue_EncodingType_EVEN_BITS, 32, 0, 2},
+    {AutofillRandomizedValue_EncodingType_ODD_BITS, 32, 1, 2},
+
+    // All bits per byte. This require 64 bytes to encode and has a 1-bit
+    // stride.
+    {AutofillRandomizedValue_EncodingType_ALL_BITS, 64, 0, 1},
+};
+
+// Size related constants.
+constexpr size_t kBitsPerByte = 8;
+constexpr size_t kMaxEncodedLengthInBytes = 64;
+constexpr size_t kMaxEncodedLengthInBits =
+    kMaxEncodedLengthInBytes * kBitsPerByte;
+
+// Find the EncodingInfo struct for |encoding_type|, else return nullptr.
+const RandomizedEncoder::EncodingInfo* GetEncodingInfo(
+    AutofillRandomizedValue_EncodingType encoding_type) {
+  DCHECK(std::is_sorted(std::begin(kEncodingInfo), std::end(kEncodingInfo),
+                        [](const RandomizedEncoder::EncodingInfo& lhs,
+                           const RandomizedEncoder::EncodingInfo& rhs) {
+                          return lhs.encoding_type < rhs.encoding_type;
+                        }));
+
+  const auto* encode_info = std::lower_bound(
+      std::begin(kEncodingInfo), std::end(kEncodingInfo), encoding_type,
+      [](const RandomizedEncoder::EncodingInfo& lhs,
+         AutofillRandomizedValue_EncodingType encoding_type) {
+        return lhs.encoding_type < encoding_type;
+      });
+
+  return (encode_info != std::end(kEncodingInfo) &&
+          encode_info->encoding_type == encoding_type)
+             ? encode_info
+             : nullptr;
+}
+
+// Get the |i|-th bit of |s| where |i| counts up from the 0-bit of the first
+// character in |s|. It is expected that the caller guarantees that |i| is a
+// valid bit-offset into |s|
+inline uint8_t GetBit(base::StringPiece s, size_t i) {
+  DCHECK_LT(i / kBitsPerByte, s.length());
+  return static_cast<bool>((s[i / kBitsPerByte]) & (1 << (i % kBitsPerByte)));
+}
+
+// Set the |i|-th bit of |s| where |i| counts up from the 0-bit of the first
+// character in |s|. It is expected that the caller guarantees that |i| is a
+// valid bit-offset into |s|.
+inline void SetBit(size_t i, uint8_t bit_value, std::string* s) {
+  DCHECK(bit_value == 0u || bit_value == 1u);
+  DCHECK(s);
+  DCHECK_LT(i / kBitsPerByte, s->length());
+
+  // Clear the target bit value.
+  (*s)[i / kBitsPerByte] &= ~(1 << (i % kBitsPerByte));
+
+  // Set the target bit to the input bit-value.
+  (*s)[i / kBitsPerByte] |= (bit_value << (i % kBitsPerByte));
+}
+// Returns a pseudo-random value of length |kMaxEncodedLengthInBytes| that is
+// derived from |secret|, |purpose|, |form_signature|, |field_signature| and
+// |data_type|.
+std::string GetPseudoRandomBits(base::StringPiece secret,
+                                base::StringPiece purpose,
+                                FormSignature form_signature,
+                                FieldSignature field_signature,
+                                base::StringPiece data_type) {
+  // The purpose and data_type strings are expect to be small semantic
+  // identifiers: "noise", "coins", "css_class", "html-name", "html_id", etc.
+  int purpose_length = base::checked_cast<int>(purpose.length());
+  int data_type_length = base::checked_cast<int>(data_type.length());
+
+  // Join the descriptive information about the encoding about to be performed.
+  std::string info =
+      base::StringPrintf("%d:%.*s;%08" PRIx64 ";%08" PRIx64 ";%d:%.*s",
+                         purpose_length, purpose_length, purpose.data(),
+                         form_signature, static_cast<uint64_t>(field_signature),
+                         data_type_length, data_type_length, data_type.data());
+
+  DVLOG(1) << "Generating pseudo-random bits from " << info;
+
+  // Generate the pseudo-random bits.
+  return crypto::HkdfSha256(secret, {}, info, kMaxEncodedLengthInBytes);
+}
+
+}  // namespace
+
+RandomizedEncoder::RandomizedEncoder(
+    std::string seed,
+    AutofillRandomizedValue_EncodingType encoding_type)
+    : seed_(std::move(seed)), encoding_info_(GetEncodingInfo(encoding_type)) {
+  DCHECK(encoding_info_ != nullptr);
+}
+
+std::string RandomizedEncoder::Encode(FormSignature form_signature,
+                                      FieldSignature field_signature,
+                                      base::StringPiece data_type,
+                                      base::StringPiece data_value) const {
+  if (!encoding_info_) {
+    NOTREACHED();
+    return std::string();
+  }
+
+  std::string coins = GetCoins(form_signature, field_signature, data_type);
+  std::string noise = GetNoise(form_signature, field_signature, data_type);
+
+  DCHECK_EQ(kMaxEncodedLengthInBytes, coins.length());
+  DCHECK_EQ(kMaxEncodedLengthInBytes, noise.length());
+
+  // If we're encoding the bits encoding we can simply repurpose the noise
+  // vector and use the coins vector merge in the selected data value bits.
+  // For each bit, the encoded value is the true value if the coin-toss is TRUE
+  // or the noise value if the coin-toss is FALSE. All the bits in a given byte
+  // can be computed in parallel. The trailing bytes are all noise.
+  if (encoding_info_->encoding_type ==
+      AutofillRandomizedValue_EncodingType_ALL_BITS) {
+    std::string all_bits = std::move(noise);
+    const size_t value_length =
+        std::min(data_value.length(), kMaxEncodedLengthInBytes);
+    for (size_t i = 0; i < value_length; ++i) {
+      // Initially this byte is all noise, we're replacing the bits for which
+      // the coin toss is 1 with the corresponding data_value bits, and keeping
+      // the noise bits where the coin toss is 0.
+      all_bits[i] = (data_value[i] & coins[i]) | (all_bits[i] & ~coins[i]);
+    }
+    return all_bits;
+  }
+
+  // Otherwise, pack the select the subset of bits into an output buffer.
+  // This encodes every |encoding_info_->bit_stride| bit starting from
+  // |encoding_info_->bit_offset|.
+  //
+  // For each bit, the encoded value is the true value if the coin-toss is TRUE
+  // or the noise value if the coin-toss is FALSE. All the bits in a given byte
+  // can be computed in parallel. The trailing bytes are all noise.
+  std::string output(encoding_info_->final_size, 0);
+  const size_t value_length_in_bits = data_value.length() * kBitsPerByte;
+  size_t dst_offset = 0;
+  size_t src_offset = encoding_info_->bit_offset;
+  while (src_offset < kMaxEncodedLengthInBits) {
+    uint8_t output_bit = GetBit(noise, src_offset);
+    if (src_offset < value_length_in_bits) {
+      const uint8_t coin_bit = GetBit(coins, src_offset);
+      const uint8_t data_bit = GetBit(data_value, src_offset);
+      output_bit = ((coin_bit & data_bit) | (~coin_bit & output_bit));
+    }
+    SetBit(dst_offset, output_bit, &output);
+    src_offset += encoding_info_->bit_stride;
+    dst_offset += 1;
+  }
+
+  DCHECK_EQ(dst_offset, encoding_info_->final_size * kBitsPerByte);
+  return output;
+}
+
+std::string RandomizedEncoder::GetCoins(FormSignature form_signature,
+                                        FieldSignature field_signature,
+                                        base::StringPiece data_type) const {
+  return GetPseudoRandomBits(seed_, "coins", form_signature, field_signature,
+                             data_type);
+}
+
+// Get the pseudo-random string to use at the noise bit-field.
+std::string RandomizedEncoder::GetNoise(FormSignature form_signature,
+                                        FieldSignature field_signature,
+                                        base::StringPiece data_type) const {
+  return GetPseudoRandomBits(seed_, "noise", form_signature, field_signature,
+                             data_type);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/randomized_encoder.h b/components/autofill/core/browser/randomized_encoder.h
new file mode 100644
index 0000000..0a4979b
--- /dev/null
+++ b/components/autofill/core/browser/randomized_encoder.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_RANDOMIZED_ENCODER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_RANDOMIZED_ENCODER_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "components/autofill/core/browser/proto/server.pb.h"
+#include "components/autofill/core/common/signatures_util.h"
+
+namespace autofill {
+
+// Encodes string values using the differential-privacy scheme as described
+// in go/autofill-metadata-upload (Google internal link).
+class RandomizedEncoder {
+ public:
+  struct EncodingInfo;
+
+  RandomizedEncoder(std::string seed,
+                    AutofillRandomizedValue_EncodingType encoding_type);
+
+  // Encode |data_value| using this instance's |encoding_type_|.
+  std::string Encode(FormSignature form_signature,
+                     FieldSignature field_signature,
+                     base::StringPiece data_type,
+                     base::StringPiece data_value) const;
+
+ protected:
+  // Get the pseudo-random string to use at the coin bit-field. This function
+  // is internal, but exposed here to facilitate testing.
+  std::string GetCoins(FormSignature form_signature,
+                       FieldSignature field_signature,
+                       base::StringPiece data_type) const;
+
+  // Get the pseudo-random string to use at the noise bit-field. This function
+  // is internal, but exposed here to facilitate testing.
+  std::string GetNoise(FormSignature form_signature,
+                       FieldSignature field_signature,
+                       base::StringPiece data_type) const;
+
+ private:
+  const std::string seed_;
+  const EncodingInfo* const encoding_info_;
+};
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_RANDOMIZED_ENCODER_H_
diff --git a/components/autofill/core/browser/randomized_encoder_unittest.cc b/components/autofill/core/browser/randomized_encoder_unittest.cc
new file mode 100644
index 0000000..dcbc1e5
--- /dev/null
+++ b/components/autofill/core/browser/randomized_encoder_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/randomized_encoder.h"
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr size_t kBitsPerByte = 8;
+constexpr size_t kMaxLengthInBytes = 64;
+constexpr size_t kMaxLengthInBits = kMaxLengthInBytes * kBitsPerByte;
+
+// Get the |i|-th bit of |s| where |i| counts up from the 0-bit of the first
+// character in |s|. It is expected that the caller guarantees that |i| is a
+// valid bit-offset into |s|
+bool GetBit(base::StringPiece s, size_t i) {
+  DCHECK_LT(i / kBitsPerByte, s.length());
+  return static_cast<bool>((s[i / kBitsPerByte]) & (1 << (i % kBitsPerByte)));
+}
+
+// This is a reference encoder implementation. This implementation performs the
+// all bits encoding one full byte at a time and then packs the selected bits
+// into a final output buffer.
+std::string ReferenceEncodeImpl(base::StringPiece coins,
+                                base::StringPiece noise,
+                                base::StringPiece value,
+                                size_t bit_offset,
+                                size_t bit_stride) {
+  // Encode all of the bits.
+  std::string all_bits = noise.as_string();
+  size_t value_length = std::min(value.length(), kMaxLengthInBytes);
+  for (size_t i = 0; i < value_length; ++i) {
+    all_bits[i] = (value[i] & coins[i]) | (all_bits[i] & ~coins[i]);
+  }
+
+  // Select the only the ones matching bit_offset and bit_stride.
+  std::string output(kMaxLengthInBytes / bit_stride, 0);
+  size_t src_offset = bit_offset;
+  size_t dst_offset = 0;
+  while (src_offset < kMaxLengthInBits) {
+    bool bit_value = GetBit(all_bits, src_offset);
+    output[dst_offset / kBitsPerByte] |=
+        (bit_value << (dst_offset % kBitsPerByte));
+    src_offset += bit_stride;
+    dst_offset += 1;
+  }
+  return output;
+}
+
+// A test version of the RandomizedEncoder class. Exposes "ForTest" methods.
+class TestRandomizedEncoder : public autofill::RandomizedEncoder {
+ public:
+  using RandomizedEncoder::RandomizedEncoder;
+  using RandomizedEncoder::GetCoins;
+  using RandomizedEncoder::GetNoise;
+};
+
+// Data structure used to drive the encoding test cases.
+struct EncodeParams {
+  // The type of encoding to perform with the RandomizedEncoder.
+  autofill::AutofillRandomizedValue_EncodingType encoding_type;
+
+  // The bit offset to start from with the reference encoder.
+  size_t bit_offset;
+
+  // The bit stride to select the next bit to encode with the reference encoder.
+  size_t bit_stride;
+};
+
+// A table to test cases, mapping encoding scheme to the reference encoder.
+const EncodeParams kEncodeParams[] = {
+    // One bit per byte. These all require 8 bytes to encode and have 8-bit
+    // strides, starting from a different initial bit offset.
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_0, 0, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_1, 1, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_2, 2, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_3, 3, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_4, 4, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_5, 5, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_6, 6, 8},
+    {autofill::AutofillRandomizedValue_EncodingType_BIT_7, 7, 8},
+
+    // Four bits per byte. These require 32 bytes to encode and have 2-bit
+    // strides/
+    {autofill::AutofillRandomizedValue_EncodingType_EVEN_BITS, 0, 2},
+    {autofill::AutofillRandomizedValue_EncodingType_ODD_BITS, 1, 2},
+
+    // All bits per byte. This require 64 bytes to encode and has a 1-bit
+    // stride.
+    {autofill::AutofillRandomizedValue_EncodingType_ALL_BITS, 0u, 1},
+};
+
+using RandomizedEncoderTest = ::testing::TestWithParam<EncodeParams>;
+
+}  // namespace
+
+TEST_P(RandomizedEncoderTest, Encode) {
+  const autofill::FormSignature form_signature = 0x1234567812345678;
+  const autofill::FieldSignature field_signature = 0xCAFEBABE;
+  const std::string data_type = "css_class";
+  const EncodeParams& params = GetParam();
+  const std::string value("This is some text for testing purposes.");
+
+  EXPECT_LT(value.length(), kMaxLengthInBytes);
+
+  TestRandomizedEncoder encoder("this is a secret", params.encoding_type);
+
+  // Encode the output string.
+  std::string actual_result =
+      encoder.Encode(form_signature, field_signature, data_type, value);
+
+  // Capture the coin and noise bits used for the form, field and metadata type.
+  std::string coins =
+      encoder.GetCoins(form_signature, field_signature, data_type);
+  std::string noise =
+      encoder.GetNoise(form_signature, field_signature, data_type);
+
+  // Use the reference encoder implementation to get the expected output.
+  std::string expected_result = ReferenceEncodeImpl(
+      coins, noise, value, params.bit_offset, params.bit_stride);
+
+  // The results should be the same.
+  EXPECT_EQ(kMaxLengthInBytes / params.bit_stride, actual_result.length());
+  EXPECT_EQ(expected_result, actual_result);
+}
+
+TEST_P(RandomizedEncoderTest, EncodeLarge) {
+  const autofill::FormSignature form_signature = 0x8765432187654321;
+  const autofill::FieldSignature field_signature = 0xDEADBEEF;
+  const std::string data_type = "html_name";
+  const EncodeParams& params = GetParam();
+  const std::string value(
+      "This is some text for testing purposes. It exceeds the maximum encoding "
+      "size. This serves to validate that truncation is performed. Lots and "
+      " or text. Yay!");
+  EXPECT_GT(value.length(), kMaxLengthInBytes);
+
+  TestRandomizedEncoder encoder("this is a secret", params.encoding_type);
+
+  // Encode the output string.
+  std::string actual_result =
+      encoder.Encode(form_signature, field_signature, data_type, value);
+
+  // Capture the coin and noise bits used for the form, field and metadata type.
+  std::string coins =
+      encoder.GetCoins(form_signature, field_signature, data_type);
+  std::string noise =
+      encoder.GetNoise(form_signature, field_signature, data_type);
+
+  // Use the reference encoder implementation to get the expected output.
+  std::string expected_result = ReferenceEncodeImpl(
+      coins, noise, value, params.bit_offset, params.bit_stride);
+
+  // The results should be the same.
+  EXPECT_EQ(kMaxLengthInBytes / params.bit_stride, actual_result.length());
+  EXPECT_EQ(expected_result, actual_result);
+}
+
+INSTANTIATE_TEST_CASE_P(All,
+                        RandomizedEncoderTest,
+                        ::testing::ValuesIn(kEncodeParams));
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 5741b35..524658d 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -21,6 +21,13 @@
 namespace autofill {
 namespace features {
 
+// Controls whether autofill activates on non-HTTP(S) pages. Useful for
+// automated with data URLS in cases where it's too difficult to use the
+// embedded test server. Generally avoid using.
+//
+const base::Feature kAutofillAllowNonHttpActivation{
+    "AutofillAllowNonHttpActivation", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether the AddressNormalizer is supplied. If available, it may be
 // used to normalize address and will incur fetching rules from the server.
 const base::Feature kAutofillAddressNormalizer{
@@ -147,7 +154,7 @@
 // Controls whether Autofill should rationalize repeated server type
 // predictions.
 const base::Feature kAutofillRationalizeRepeatedServerPredictions{
-    "kAutofillRationalizeRepeatedServerPredictions",
+    "AutofillRationalizeRepeatedServerPredictions",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether Full Server credit cards should be reset when the sync
@@ -204,7 +211,7 @@
 // "upload" resources.
 // i.e., https://other.autofill.server:port/tbproxy/af/
 const base::Feature kAutofillServerCommunication{
-    "kAutofillServerCommunication", base::FEATURE_ENABLED_BY_DEFAULT};
+    "AutofillServerCommunication", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether autofill suggestions are filtered by field values previously
 // filled by website.
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 589a2da..48400782 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -21,6 +21,7 @@
 namespace features {
 
 // All features in alphabetical order.
+extern const base::Feature kAutofillAllowNonHttpActivation;
 extern const base::Feature kAutofillAddressNormalizer;
 extern const base::Feature kAutofillAlwaysFillAddresses;
 extern const base::Feature kAutofillCacheQueryResponses;
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index c13bce3a..831c5dd 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -281,6 +281,18 @@
     }
   }
 
+  // Clear the status message or show the specified prompt.
+  std::string prompt;
+  for (const auto& script : runnable_scripts) {
+    // runnable_scripts is ordered by priority.
+    if (!script.initial_prompt.empty()) {
+      prompt = script.initial_prompt;
+      break;
+    }
+  }
+  GetUiController()->ShowStatusMessage(prompt);
+
+  // Update the set of scripts.
   GetUiController()->UpdateScripts(runnable_scripts);
 }
 
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 345b240..c3619dc 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -187,6 +187,35 @@
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
 }
 
+TEST_F(ControllerTest, ShowFirstInitialPrompt) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "script1");
+
+  SupportedScriptProto* script2 =
+      AddRunnableScript(&script_response, "script2");
+  script2->mutable_presentation()->set_initial_prompt("script2 prompt");
+  script2->mutable_presentation()->set_priority(10);
+
+  SupportedScriptProto* script3 =
+      AddRunnableScript(&script_response, "script3");
+  script3->mutable_presentation()->set_initial_prompt("script3 prompt");
+  script3->mutable_presentation()->set_priority(5);
+
+  SupportedScriptProto* script4 =
+      AddRunnableScript(&script_response, "script4");
+  script4->mutable_presentation()->set_initial_prompt("script4 prompt");
+  script4->mutable_presentation()->set_priority(8);
+
+  SetNextScriptResponse(script_response);
+
+  // Script3, with higher priority (lower number), wins.
+  EXPECT_CALL(*mock_ui_controller_, ShowStatusMessage("script3 prompt"));
+  EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(4)));
+
+  // Start the flow.
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+}
+
 TEST_F(ControllerTest, Stop) {
   ActionsResponseProto actions_response;
   actions_response.add_actions()->mutable_stop();
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 975a4dd3..1b2935b 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -80,6 +80,7 @@
     const auto& presentation = script_proto.presentation();
     script->handle.name = presentation.name();
     script->handle.autostart = presentation.autostart();
+    script->handle.initial_prompt = presentation.initial_prompt();
     script->precondition = ScriptPrecondition::FromProto(
         script_proto.path(), presentation.precondition());
     script->priority = presentation.priority();
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 605e77a..ea5d526 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -52,6 +52,7 @@
   auto* presentation = script->mutable_presentation();
   presentation->set_name("name");
   presentation->set_autostart(true);
+  presentation->set_initial_prompt("prompt");
   presentation->mutable_precondition()->add_domain("www.example.com");
 
   std::vector<std::unique_ptr<Script>> scripts;
@@ -61,6 +62,7 @@
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
   EXPECT_EQ("name", scripts[0]->handle.name);
+  EXPECT_EQ("prompt", scripts[0]->handle.initial_prompt);
   EXPECT_TRUE(scripts[0]->handle.autostart);
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
diff --git a/components/autofill_assistant/browser/script.cc b/components/autofill_assistant/browser/script.cc
index 62ec03f..1efb8fe 100644
--- a/components/autofill_assistant/browser/script.cc
+++ b/components/autofill_assistant/browser/script.cc
@@ -8,6 +8,8 @@
 
 ScriptHandle::ScriptHandle() : autostart(false) {}
 
+ScriptHandle::ScriptHandle(const ScriptHandle& orig) = default;
+
 ScriptHandle::~ScriptHandle() = default;
 
 Script::Script() : priority(0) {}
diff --git a/components/autofill_assistant/browser/script.h b/components/autofill_assistant/browser/script.h
index a446f6b..0a0779b 100644
--- a/components/autofill_assistant/browser/script.h
+++ b/components/autofill_assistant/browser/script.h
@@ -15,10 +15,12 @@
 // Minimal information about a script necessary to display and run it.
 struct ScriptHandle {
   ScriptHandle();
+  ScriptHandle(const ScriptHandle& orig);
   ~ScriptHandle();
 
   std::string name;
   std::string path;
+  std::string initial_prompt;
 
   // When set to true this script can be run in 'autostart mode'. Script won't
   // be shown.
diff --git a/components/autofill_assistant/browser/script_precondition.cc b/components/autofill_assistant/browser/script_precondition.cc
index 68948ea8..b9f3430b 100644
--- a/components/autofill_assistant/browser/script_precondition.cc
+++ b/components/autofill_assistant/browser/script_precondition.cc
@@ -121,7 +121,9 @@
     return true;
   }
 
-  const std::string path = url.path();
+  std::string path = url.has_ref()
+                         ? base::StrCat({url.PathForRequest(), "#", url.ref()})
+                         : url.PathForRequest();
   for (auto& regexp : path_pattern_) {
     if (regexp->Match(path, 0, path.size(), re2::RE2::UNANCHORED, NULL, 0)) {
       return true;
diff --git a/components/autofill_assistant/browser/script_precondition_unittest.cc b/components/autofill_assistant/browser/script_precondition_unittest.cc
index bc9da25..27e11513 100644
--- a/components/autofill_assistant/browser/script_precondition_unittest.cc
+++ b/components/autofill_assistant/browser/script_precondition_unittest.cc
@@ -157,6 +157,17 @@
   EXPECT_TRUE(Check(proto));
 }
 
+TEST_F(ScriptPreconditionTest, PathWithQueryAndRef) {
+  ScriptPreconditionProto proto;
+  proto.add_path_pattern("/hello.*world");
+
+  SetUrl("http://www.example.com/hello?q=world");
+  EXPECT_TRUE(Check(proto));
+
+  SetUrl("http://www.example.com/hello#world");
+  EXPECT_TRUE(Check(proto));
+}
+
 TEST_F(ScriptPreconditionTest, BadPathPattern) {
   ScriptPreconditionProto proto;
   proto.add_path_pattern("invalid[");
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 25a55d6..e07c8ff9 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -48,6 +48,11 @@
     // be executed. No precondition means that a script can run in any case.
     optional ScriptPreconditionProto precondition = 3;
 
+    // Text display at the same time as the script is proposed. If more than one
+    // script has an initial prompt, the prompt of the highest-priority script
+    // wins.
+    optional string initial_prompt = 4;
+
     // Display priority of the script. Lowest number has highest priority, which
     // means a script with priority 0 should be displayed before a script with
     // priority 1.
@@ -98,7 +103,7 @@
 message ScriptPreconditionProto {
   // Combined with AND: the elements referenced here must be present.
   repeated ElementReferenceProto elements_exist = 3;
-  // Pattern of the path parts of the URL.
+  // Pattern of the path parts of the URL, including query and '#''.
   repeated string path_pattern = 5;
   // Domain (exact match) excluding the last '/' character.
   repeated string domain = 6;
diff --git a/components/component_updater/component_updater_service.h b/components/component_updater/component_updater_service.h
index 3ef6a1d1..03bcb3c 100644
--- a/components/component_updater/component_updater_service.h
+++ b/components/component_updater/component_updater_service.h
@@ -166,7 +166,7 @@
   friend class ::PluginObserver;
   friend class SwReporterOnDemandFetcher;
 #if defined(OS_CHROMEOS)
-  friend class CrOSComponentManager;
+  friend class CrOSComponentInstaller;
 #endif  // defined(OS_CHROMEOS)
   friend class VrAssetsComponentInstallerPolicy;
 
diff --git a/components/leveldb_proto/BUILD.gn b/components/leveldb_proto/BUILD.gn
index 9405633..523c133a 100644
--- a/components/leveldb_proto/BUILD.gn
+++ b/components/leveldb_proto/BUILD.gn
@@ -9,6 +9,9 @@
     "proto_database.cc",
     "proto_database.h",
     "proto_database_impl.h",
+    "proto_leveldb_wrapper.cc",
+    "proto_leveldb_wrapper.h",
+    "unique_proto_database.h",
   ]
 
   public_deps = [
@@ -26,6 +29,7 @@
   public_deps = [
     ":leveldb_proto",
     "//base",
+    "//base/test:test_support",
     "//components/leveldb_proto/testing/proto",
   ]
 }
@@ -33,7 +37,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "proto_database_impl_unittest.cc",
+    "unique_proto_database_unittest.cc",
   ]
   deps = [
     ":test_support",
diff --git a/components/leveldb_proto/leveldb_database.h b/components/leveldb_proto/leveldb_database.h
index 2444e38..a226005c0 100644
--- a/components/leveldb_proto/leveldb_database.h
+++ b/components/leveldb_proto/leveldb_database.h
@@ -10,10 +10,7 @@
 #include <string>
 #include <vector>
 
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
 #include "base/strings/string_split.h"
-#include "base/threading/thread_collision_warner.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 namespace base {
@@ -25,7 +22,6 @@
 class Cache;
 class DB;
 class Env;
-class Status;
 }  // namespace leveldb
 
 namespace leveldb_proto {
diff --git a/components/leveldb_proto/proto_database.h b/components/leveldb_proto/proto_database.h
index b62bd4f..7f7d321 100644
--- a/components/leveldb_proto/proto_database.h
+++ b/components/leveldb_proto/proto_database.h
@@ -6,18 +6,12 @@
 #define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
 
 #include <map>
-#include <memory>
-#include <string>
-#include <utility>
 #include <vector>
 
-#include "base/callback.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_checker.h"
 #include "components/leveldb_proto/leveldb_database.h"
-#include "third_party/leveldatabase/env_chromium.h"
-
-namespace base {
-class FilePath;
-}
+#include "components/leveldb_proto/proto_leveldb_wrapper.h"
 
 namespace leveldb_proto {
 
@@ -43,6 +37,9 @@
   // A list of key-value (string, T) tuples.
   using KeyEntryVector = std::vector<std::pair<std::string, T>>;
 
+  explicit ProtoDatabase(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+      : db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner)) {}
   virtual ~ProtoDatabase() {}
 
   // Asynchronously initializes the object with the specified |options|.
@@ -50,7 +47,15 @@
   virtual void Init(const char* client_name,
                     const base::FilePath& database_dir,
                     const leveldb_env::Options& options,
-                    InitCallback callback) = 0;
+                    typename ProtoDatabase<T>::InitCallback callback) = 0;
+
+  virtual void InitWithDatabase(LevelDB* database,
+                                const base::FilePath& database_dir,
+                                const leveldb_env::Options& options,
+                                InitCallback callback) {
+    db_wrapper_->InitWithDatabase(database, database_dir, options,
+                                  std::move(callback));
+  }
 
   // Asynchronously saves |entries_to_save| and deletes entries from
   // |keys_to_remove| from the database. |callback| will be invoked on the
@@ -58,7 +63,11 @@
   virtual void UpdateEntries(
       std::unique_ptr<KeyEntryVector> entries_to_save,
       std::unique_ptr<std::vector<std::string>> keys_to_remove,
-      UpdateCallback callback) = 0;
+      UpdateCallback callback) {
+    db_wrapper_->template UpdateEntries<T>(std::move(entries_to_save),
+                                           std::move(keys_to_remove),
+                                           std::move(callback));
+  }
 
   // Asynchronously saves |entries_to_save| and deletes entries that satisfies
   // the |delete_key_filter| from the database. |callback| will be invoked on
@@ -67,44 +76,76 @@
   virtual void UpdateEntriesWithRemoveFilter(
       std::unique_ptr<KeyEntryVector> entries_to_save,
       const LevelDB::KeyFilter& delete_key_filter,
-      UpdateCallback callback) = 0;
+      UpdateCallback callback) {
+    db_wrapper_->template UpdateEntriesWithRemoveFilter<T>(
+        std::move(entries_to_save), delete_key_filter, std::move(callback));
+  }
 
   // Asynchronously loads all entries from the database and invokes |callback|
   // when complete.
-  virtual void LoadEntries(LoadCallback callback) = 0;
+  virtual void LoadEntries(LoadCallback callback) {
+    db_wrapper_->template LoadEntries<T>(std::move(callback));
+  }
 
   // Asynchronously loads entries that satisfies the |filter| from the database
   // and invokes |callback| when complete. The filter will be called on
   // ProtoDatabase's taskrunner.
   virtual void LoadEntriesWithFilter(const LevelDB::KeyFilter& filter,
-                                     LoadCallback callback) = 0;
-  virtual void LoadEntriesWithFilter(const LevelDB::KeyFilter& filter,
+                                     LoadCallback callback) {
+    db_wrapper_->template LoadEntriesWithFilter<T>(filter, std::move(callback));
+  }
+
+  virtual void LoadEntriesWithFilter(const LevelDB::KeyFilter& key_filter,
                                      const leveldb::ReadOptions& options,
                                      const std::string& target_prefix,
-                                     LoadCallback callback) = 0;
+                                     LoadCallback callback) {
+    db_wrapper_->template LoadEntriesWithFilter<T>(
+        key_filter, options, target_prefix, std::move(callback));
+  }
 
-  virtual void LoadKeysAndEntries(
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) = 0;
+  virtual void LoadKeysAndEntries(LoadKeysAndEntriesCallback callback) {
+    db_wrapper_->template LoadKeysAndEntries<T>(std::move(callback));
+  }
+
   virtual void LoadKeysAndEntriesWithFilter(
       const LevelDB::KeyFilter& filter,
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) = 0;
+      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) {
+    db_wrapper_->template LoadKeysAndEntriesWithFilter<T>(filter,
+                                                          std::move(callback));
+  }
   virtual void LoadKeysAndEntriesWithFilter(
       const LevelDB::KeyFilter& filter,
       const leveldb::ReadOptions& options,
       const std::string& target_prefix,
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) = 0;
+      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) {
+    db_wrapper_->template LoadKeysAndEntriesWithFilter<T>(
+        filter, options, target_prefix, std::move(callback));
+  }
 
   // Asynchronously loads all keys from the database and invokes |callback| with
   // those keys when complete.
-  virtual void LoadKeys(LoadKeysCallback callback) = 0;
+  virtual void LoadKeys(LoadKeysCallback callback) {
+    db_wrapper_->LoadKeys(std::move(callback));
+  }
 
   // Asynchronously loads a single entry, identified by |key|, from the database
   // and invokes |callback| when complete. If no entry with |key| is found,
   // a nullptr is passed to the callback, but the success flag is still true.
-  virtual void GetEntry(const std::string& key, GetCallback callback) = 0;
+  virtual void GetEntry(const std::string& key, GetCallback callback) {
+    db_wrapper_->template GetEntry<T>(key, std::move(callback));
+  }
 
   // Asynchronously destroys the database.
-  virtual void Destroy(DestroyCallback callback) = 0;
+  virtual void Destroy(DestroyCallback callback) {
+    db_wrapper_->Destroy(std::move(callback));
+  }
+
+  bool GetApproximateMemoryUse(uint64_t* approx_mem_use) {
+    return db_wrapper_->GetApproximateMemoryUse(approx_mem_use);
+  }
+
+ protected:
+  std::unique_ptr<ProtoLevelDBWrapper> db_wrapper_;
 };
 
 // Return a new instance of Options, but with two additions:
@@ -114,4 +155,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_database_impl.h b/components/leveldb_proto/proto_database_impl.h
index b7a2202..def7f23 100644
--- a/components/leveldb_proto/proto_database_impl.h
+++ b/components/leveldb_proto/proto_database_impl.h
@@ -5,483 +5,16 @@
 #ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
 #define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
 
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/threading/thread_checker.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/unique_proto_database.h"
 
 namespace leveldb_proto {
 
-using KeyValueVector = base::StringPairs;
-using KeyVector = std::vector<std::string>;
-
-// When the ProtoDatabaseImpl instance is deleted, in-progress asynchronous
-// operations will be completed and the corresponding callbacks will be called.
-// Construction/calls/destruction should all happen on the same thread.
+// Part of an ongoing effort to refactor ProtoDatabase. New users of
+// ProtoDatabaseImpl should avoid using this in favor of including
+// unique_proto_database.h and using UniqueProtoDatabase directly.
+// See https://crbug.com/870813.
 template <typename T>
-class ProtoDatabaseImpl : public ProtoDatabase<T> {
- public:
-  // All blocking calls/disk access will happen on the provided |task_runner|.
-  explicit ProtoDatabaseImpl(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
-
-  ~ProtoDatabaseImpl() override;
-
-  // ProtoDatabase implementation.
-  // TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
-  //     part of the constructor
-  void Init(const char* client_name,
-            const base::FilePath& database_dir,
-            const leveldb_env::Options& options,
-            typename ProtoDatabase<T>::InitCallback callback) override;
-  void UpdateEntries(
-      std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
-          entries_to_save,
-      std::unique_ptr<KeyVector> keys_to_remove,
-      typename ProtoDatabase<T>::UpdateCallback callback) override;
-  void UpdateEntriesWithRemoveFilter(
-      std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
-          entries_to_save,
-      const LevelDB::KeyFilter& delete_key_filter,
-      typename ProtoDatabase<T>::UpdateCallback callback) override;
-  void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
-  void LoadEntriesWithFilter(
-      const LevelDB::KeyFilter& filter,
-      typename ProtoDatabase<T>::LoadCallback callback) override;
-  void LoadEntriesWithFilter(
-      const LevelDB::KeyFilter& filter,
-      const leveldb::ReadOptions& options,
-      const std::string& target_prefix,
-      typename ProtoDatabase<T>::LoadCallback callback) override;
-  void LoadKeysAndEntries(
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
-  void LoadKeysAndEntriesWithFilter(
-      const LevelDB::KeyFilter& filter,
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
-  void LoadKeysAndEntriesWithFilter(
-      const LevelDB::KeyFilter& filter,
-      const leveldb::ReadOptions& options,
-      const std::string& target_prefix,
-      typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
-  void LoadKeys(typename ProtoDatabase<T>::LoadKeysCallback callback) override;
-  void GetEntry(const std::string& key,
-                typename ProtoDatabase<T>::GetCallback callback) override;
-  void Destroy(typename ProtoDatabase<T>::DestroyCallback callback) override;
-
-  // Allow callers to provide their own Database implementation.
-  void InitWithDatabase(std::unique_ptr<LevelDB> database,
-                        const base::FilePath& database_dir,
-                        const leveldb_env::Options& options,
-                        typename ProtoDatabase<T>::InitCallback callback);
-
-  bool GetApproximateMemoryUse(uint64_t* approx_mem);
-
- private:
-  base::ThreadChecker thread_checker_;
-
-  // Used to run blocking tasks in-order.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  std::unique_ptr<LevelDB> db_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProtoDatabaseImpl);
-};
-
-namespace {
-
-template <typename T>
-void RunInitCallback(typename ProtoDatabase<T>::InitCallback callback,
-                     const bool* success) {
-  std::move(callback).Run(*success);
-}
-
-template <typename T>
-void RunUpdateCallback(typename ProtoDatabase<T>::UpdateCallback callback,
-                       const bool* success) {
-  std::move(callback).Run(*success);
-}
-
-template <typename T>
-void RunLoadCallback(typename ProtoDatabase<T>::LoadCallback callback,
-                     bool* success,
-                     std::unique_ptr<std::vector<T>> entries) {
-  std::move(callback).Run(*success, std::move(entries));
-}
-
-template <typename T>
-void RunLoadKeysAndEntriesCallback(
-    typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback,
-    bool* success,
-    std::unique_ptr<std::map<std::string, T>> keys_entries) {
-  std::move(callback).Run(*success, std::move(keys_entries));
-}
-
-template <typename T>
-void RunLoadKeysCallback(typename ProtoDatabase<T>::LoadKeysCallback callback,
-                         std::unique_ptr<bool> success,
-                         std::unique_ptr<std::vector<std::string>> keys) {
-  std::move(callback).Run(*success, std::move(keys));
-}
-
-template <typename T>
-void RunGetCallback(typename ProtoDatabase<T>::GetCallback callback,
-                    const bool* success,
-                    const bool* found,
-                    std::unique_ptr<T> entry) {
-  std::move(callback).Run(*success, *found ? std::move(entry) : nullptr);
-}
-
-template <typename T>
-void RunDestroyCallback(typename ProtoDatabase<T>::DestroyCallback callback,
-                        const bool* success) {
-  std::move(callback).Run(*success);
-}
-
-inline void InitFromTaskRunner(LevelDB* database,
-                               const base::FilePath& database_dir,
-                               const leveldb_env::Options& options,
-                               bool* success) {
-  DCHECK(success);
-
-  // TODO(cjhopman): Histogram for database size.
-  *success = database->Init(database_dir, options);
-}
-
-inline void DestroyFromTaskRunner(std::unique_ptr<LevelDB> leveldb,
-                                  bool* success) {
-  CHECK(success);
-
-  *success = leveldb->Destroy();
-}
-
-template <typename T>
-void UpdateEntriesFromTaskRunner(
-    LevelDB* database,
-    std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
-    std::unique_ptr<KeyVector> keys_to_remove,
-    bool* success) {
-  DCHECK(success);
-
-  // Serialize the values from Proto to string before passing on to database.
-  KeyValueVector pairs_to_save;
-  for (const auto& pair : *entries_to_save) {
-    pairs_to_save.push_back(
-        std::make_pair(pair.first, pair.second.SerializeAsString()));
-  }
-
-  *success = database->Save(pairs_to_save, *keys_to_remove);
-}
-
-template <typename T>
-void UpdateEntriesWithRemoveFilterFromTaskRunner(
-    LevelDB* database,
-    std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
-    const LevelDB::KeyFilter& delete_key_filter,
-    bool* success) {
-  DCHECK(success);
-
-  // Serialize the values from Proto to string before passing on to database.
-  KeyValueVector pairs_to_save;
-  for (const auto& pair : *entries_to_save) {
-    pairs_to_save.push_back(
-        std::make_pair(pair.first, pair.second.SerializeAsString()));
-  }
-
-  *success = database->UpdateWithRemoveFilter(pairs_to_save, delete_key_filter);
-}
-
-template <typename T>
-void LoadKeysAndEntriesFromTaskRunner(LevelDB* database,
-                                      const LevelDB::KeyFilter& filter,
-                                      const leveldb::ReadOptions& options,
-                                      const std::string& target_prefix,
-                                      std::map<std::string, T>* keys_entries,
-                                      bool* success) {
-  DCHECK(success);
-  DCHECK(keys_entries);
-
-  keys_entries->clear();
-
-  std::map<std::string, std::string> loaded_entries;
-  *success = database->LoadKeysAndEntriesWithFilter(filter, &loaded_entries,
-                                                    options, target_prefix);
-
-  for (const auto& pair : loaded_entries) {
-    T entry;
-    if (!entry.ParseFromString(pair.second)) {
-      DLOG(WARNING) << "Unable to parse leveldb_proto entry";
-      // TODO(cjhopman): Decide what to do about un-parseable entries.
-    }
-
-    keys_entries->insert(std::make_pair(pair.first, entry));
-  }
-}
-
-template <typename T>
-void LoadEntriesFromTaskRunner(LevelDB* database,
-                               const LevelDB::KeyFilter& filter,
-                               const leveldb::ReadOptions& options,
-                               const std::string& target_prefix,
-                               std::vector<T>* entries,
-                               bool* success) {
-  entries->clear();
-
-  std::map<std::string, T> keys_entries;
-  LoadKeysAndEntriesFromTaskRunner<T>(database, filter, options, target_prefix,
-                                      &keys_entries, success);
-  for (const auto& pair : keys_entries)
-    entries->push_back(pair.second);
-}
-
-inline void LoadKeysFromTaskRunner(LevelDB* database,
-                                   std::vector<std::string>* keys,
-                                   bool* success) {
-  DCHECK(success);
-  DCHECK(keys);
-  keys->clear();
-  *success = database->LoadKeys(keys);
-}
-
-template <typename T>
-void GetEntryFromTaskRunner(LevelDB* database,
-                            const std::string& key,
-                            T* entry,
-                            bool* found,
-                            bool* success) {
-  DCHECK(success);
-  DCHECK(found);
-  DCHECK(entry);
-
-  std::string serialized_entry;
-  *success = database->Get(key, found, &serialized_entry);
-
-  if (!*success) {
-    *found = false;
-    return;
-  }
-
-  if (!*found)
-    return;
-
-  if (!entry->ParseFromString(serialized_entry)) {
-    *found = false;
-    DLOG(WARNING) << "Unable to parse leveldb_proto entry";
-    // TODO(cjhopman): Decide what to do about un-parseable entries.
-  }
-}
-
-}  // namespace
-
-template <typename T>
-ProtoDatabaseImpl<T>::ProtoDatabaseImpl(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
-    : task_runner_(task_runner) {}
-
-template <typename T>
-ProtoDatabaseImpl<T>::~ProtoDatabaseImpl() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (db_.get() && !task_runner_->DeleteSoon(FROM_HERE, db_.release()))
-    DLOG(WARNING) << "Proto database will not be deleted.";
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::Init(
-    const char* client_name,
-    const base::FilePath& database_dir,
-    const leveldb_env::Options& options,
-    typename ProtoDatabase<T>::InitCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  InitWithDatabase(base::WrapUnique(new LevelDB(client_name)), database_dir,
-                   options, std::move(callback));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::Destroy(
-    typename ProtoDatabase<T>::DestroyCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(db_);
-
-  bool* success = new bool(false);
-  task_runner_->PostTaskAndReply(
-      FROM_HERE, base::BindOnce(DestroyFromTaskRunner, std::move(db_), success),
-      base::BindOnce(RunDestroyCallback<T>, std::move(callback),
-                     base::Owned(success)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::InitWithDatabase(
-    std::unique_ptr<LevelDB> database,
-    const base::FilePath& database_dir,
-    const leveldb_env::Options& options,
-    typename ProtoDatabase<T>::InitCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!db_);
-  DCHECK(database);
-  db_ = std::move(database);
-  bool* success = new bool(false);
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(InitFromTaskRunner, base::Unretained(db_.get()),
-                     database_dir, options, success),
-      base::BindOnce(RunInitCallback<T>, std::move(callback),
-                     base::Owned(success)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::UpdateEntries(
-    std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
-    std::unique_ptr<KeyVector> keys_to_remove,
-    typename ProtoDatabase<T>::UpdateCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bool* success = new bool(false);
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(UpdateEntriesFromTaskRunner<T>,
-                     base::Unretained(db_.get()), std::move(entries_to_save),
-                     std::move(keys_to_remove), success),
-      base::BindOnce(RunUpdateCallback<T>, std::move(callback),
-                     base::Owned(success)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::UpdateEntriesWithRemoveFilter(
-    std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
-    const LevelDB::KeyFilter& delete_key_filter,
-    typename ProtoDatabase<T>::UpdateCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bool* success = new bool(false);
-
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner<T>,
-                     base::Unretained(db_.get()), std::move(entries_to_save),
-                     delete_key_filter, success),
-      base::BindOnce(RunUpdateCallback<T>, std::move(callback),
-                     base::Owned(success)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadEntries(
-    typename ProtoDatabase<T>::LoadCallback callback) {
-  LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadEntriesWithFilter(
-    const LevelDB::KeyFilter& key_filter,
-    typename ProtoDatabase<T>::LoadCallback callback) {
-  LoadEntriesWithFilter(key_filter, leveldb::ReadOptions(), std::string(),
-                        std::move(callback));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadEntriesWithFilter(
-    const LevelDB::KeyFilter& key_filter,
-    const leveldb::ReadOptions& options,
-    const std::string& target_prefix,
-    typename ProtoDatabase<T>::LoadCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bool* success = new bool(false);
-
-  auto entries = std::make_unique<std::vector<T>>();
-  // Get this pointer before entries is std::move()'d so we can use it below.
-  std::vector<T>* entries_ptr = entries.get();
-
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(LoadEntriesFromTaskRunner<T>, base::Unretained(db_.get()),
-                     key_filter, options, target_prefix, entries_ptr, success),
-      base::BindOnce(RunLoadCallback<T>, std::move(callback),
-                     base::Owned(success), std::move(entries)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadKeysAndEntries(
-    typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) {
-  LoadKeysAndEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadKeysAndEntriesWithFilter(
-    const LevelDB::KeyFilter& key_filter,
-    typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) {
-  LoadKeysAndEntriesWithFilter(key_filter, leveldb::ReadOptions(),
-                               std::string(), std::move(callback));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadKeysAndEntriesWithFilter(
-    const LevelDB::KeyFilter& key_filter,
-    const leveldb::ReadOptions& options,
-    const std::string& target_prefix,
-    typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bool* success = new bool(false);
-
-  auto keys_entries = std::make_unique<std::map<std::string, T>>();
-  // Get this pointer before entries is std::move()'d so we can use it below.
-  std::map<std::string, T>* keys_entries_ptr = keys_entries.get();
-
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(LoadKeysAndEntriesFromTaskRunner<T>,
-                     base::Unretained(db_.get()), key_filter, options,
-                     target_prefix, keys_entries_ptr, success),
-      base::BindOnce(RunLoadKeysAndEntriesCallback<T>, std::move(callback),
-                     base::Owned(success), std::move(keys_entries)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::LoadKeys(
-    typename ProtoDatabase<T>::LoadKeysCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  auto success = std::make_unique<bool>(false);
-  auto keys = std::make_unique<std::vector<std::string>>();
-  auto load_task =
-      base::Bind(LoadKeysFromTaskRunner, base::Unretained(db_.get()),
-                 keys.get(), success.get());
-  task_runner_->PostTaskAndReply(
-      FROM_HERE, load_task,
-      base::BindOnce(RunLoadKeysCallback<T>, std::move(callback),
-                     std::move(success), std::move(keys)));
-}
-
-template <typename T>
-void ProtoDatabaseImpl<T>::GetEntry(
-    const std::string& key,
-    typename ProtoDatabase<T>::GetCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bool* success = new bool(false);
-  bool* found = new bool(false);
-
-  std::unique_ptr<T> entry(new T());
-  // Get this pointer before entry is std::move()'d so we can use it below.
-  T* entry_ptr = entry.get();
-
-  task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(GetEntryFromTaskRunner<T>, base::Unretained(db_.get()),
-                     key, entry_ptr, found, success),
-      base::BindOnce(RunGetCallback<T>, std::move(callback),
-                     base::Owned(success), base::Owned(found),
-                     std::move(entry)));
-}
-
-template <typename T>
-bool ProtoDatabaseImpl<T>::GetApproximateMemoryUse(uint64_t* approx_mem) {
-  return db_->GetApproximateMemoryUse(approx_mem);
-}
+using ProtoDatabaseImpl = UniqueProtoDatabase<T>;
 
 }  // namespace leveldb_proto
 
diff --git a/components/leveldb_proto/proto_database_perftest.cc b/components/leveldb_proto/proto_database_perftest.cc
index 9152e726..4dbc010 100644
--- a/components/leveldb_proto/proto_database_perftest.cc
+++ b/components/leveldb_proto/proto_database_perftest.cc
@@ -24,8 +24,8 @@
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_database_impl.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
+#include "components/leveldb_proto/unique_proto_database.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
@@ -85,7 +85,7 @@
   TestDatabase(const std::string& name,
                scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                const base::FilePath& path) {
-    db_.reset(new ProtoDatabaseImpl<TestProto>(task_runner));
+    db_.reset(new UniqueProtoDatabase<TestProto>(task_runner));
     leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
 
     base::RunLoop run_init_db;
@@ -102,11 +102,11 @@
   }
 
   bool is_initialized() const { return is_initialized_; }
-  ProtoDatabaseImpl<TestProto>* proto_db() const { return db_.get(); }
+  UniqueProtoDatabase<TestProto>* proto_db() const { return db_.get(); }
 
  private:
   bool is_initialized_ = false;
-  std::unique_ptr<ProtoDatabaseImpl<TestProto>> db_;
+  std::unique_ptr<UniqueProtoDatabase<TestProto>> db_;
 };
 
 }  // namespace
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.cc b/components/leveldb_proto/proto_leveldb_wrapper.cc
new file mode 100644
index 0000000..e82e4ae
--- /dev/null
+++ b/components/leveldb_proto/proto_leveldb_wrapper.cc
@@ -0,0 +1,114 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+
+namespace leveldb_proto {
+
+namespace {
+
+void RunInitCallback(typename ProtoLevelDBWrapper::InitCallback callback,
+                     const bool* success) {
+  std::move(callback).Run(*success);
+}
+
+inline void InitFromTaskRunner(LevelDB* database,
+                               const base::FilePath& database_dir,
+                               const leveldb_env::Options& options,
+                               bool* success) {
+  DCHECK(success);
+
+  // TODO(cjhopman): Histogram for database size.
+  *success = database->Init(database_dir, options);
+}
+
+void RunDestroyCallback(typename ProtoLevelDBWrapper::DestroyCallback callback,
+                        const bool* success) {
+  std::move(callback).Run(*success);
+}
+
+inline void DestroyFromTaskRunner(LevelDB* database, bool* success) {
+  CHECK(success);
+
+  *success = database->Destroy();
+}
+
+void RunLoadKeysCallback(
+    typename ProtoLevelDBWrapper::LoadKeysCallback callback,
+    std::unique_ptr<bool> success,
+    std::unique_ptr<std::vector<std::string>> keys) {
+  std::move(callback).Run(*success, std::move(keys));
+}
+
+inline void LoadKeysFromTaskRunner(LevelDB* database,
+                                   std::vector<std::string>* keys,
+                                   bool* success) {
+  DCHECK(success);
+  DCHECK(keys);
+  keys->clear();
+  *success = database->LoadKeys(keys);
+}
+
+}  // namespace
+
+ProtoLevelDBWrapper::ProtoLevelDBWrapper(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : task_runner_(task_runner) {}
+
+ProtoLevelDBWrapper::~ProtoLevelDBWrapper() = default;
+
+void ProtoLevelDBWrapper::InitWithDatabase(
+    LevelDB* database,
+    const base::FilePath& database_dir,
+    const leveldb_env::Options& options,
+    typename ProtoLevelDBWrapper::InitCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(!db_);
+  DCHECK(database);
+  db_ = database;
+  bool* success = new bool(false);
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir,
+                     options, success),
+      base::BindOnce(RunInitCallback, std::move(callback),
+                     base::Owned(success)));
+}
+
+void ProtoLevelDBWrapper::Destroy(
+    typename ProtoLevelDBWrapper::DestroyCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(db_);
+
+  bool* success = new bool(false);
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(DestroyFromTaskRunner, base::Unretained(db_), success),
+      base::BindOnce(RunDestroyCallback, std::move(callback),
+                     base::Owned(success)));
+}
+
+void ProtoLevelDBWrapper::LoadKeys(
+    typename ProtoLevelDBWrapper::LoadKeysCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto success = std::make_unique<bool>(false);
+  auto keys = std::make_unique<std::vector<std::string>>();
+  auto load_task = base::BindOnce(LoadKeysFromTaskRunner, base::Unretained(db_),
+                                  keys.get(), success.get());
+  task_runner_->PostTaskAndReply(
+      FROM_HERE, std::move(load_task),
+      base::BindOnce(RunLoadKeysCallback, std::move(callback),
+                     std::move(success), std::move(keys)));
+}
+
+bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
+  return db_->GetApproximateMemoryUse(approx_mem_use);
+}
+
+const scoped_refptr<base::SequencedTaskRunner>&
+ProtoLevelDBWrapper::task_runner() {
+  return task_runner_;
+}
+
+}  // namespace leveldb_proto
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.h b/components/leveldb_proto/proto_leveldb_wrapper.h
new file mode 100644
index 0000000..0a2be1a
--- /dev/null
+++ b/components/leveldb_proto/proto_leveldb_wrapper.h
@@ -0,0 +1,430 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
+#define COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "components/leveldb_proto/leveldb_database.h"
+#include "third_party/leveldatabase/env_chromium.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace leveldb_proto {
+
+using KeyValueVector = base::StringPairs;
+using KeyVector = std::vector<std::string>;
+
+// When the ProtoDatabase instance is deleted, in-progress asynchronous
+// operations will be completed and the corresponding callbacks will be called.
+// Construction/calls/destruction should all happen on the same thread.
+class ProtoLevelDBWrapper {
+ public:
+  using InitCallback = base::OnceCallback<void(bool success)>;
+  using UpdateCallback = base::OnceCallback<void(bool success)>;
+  using LoadKeysCallback =
+      base::OnceCallback<void(bool success,
+                              std::unique_ptr<std::vector<std::string>>)>;
+  using DestroyCallback = base::OnceCallback<void(bool success)>;
+  using OnCreateCallback = base::OnceCallback<void(ProtoLevelDBWrapper*)>;
+
+  template <typename T>
+  class Internal {
+   public:
+    using LoadCallback =
+        base::OnceCallback<void(bool success, std::unique_ptr<std::vector<T>>)>;
+    using GetCallback =
+        base::OnceCallback<void(bool success, std::unique_ptr<T>)>;
+    using LoadKeysAndEntriesCallback =
+        base::OnceCallback<void(bool success,
+                                std::unique_ptr<std::map<std::string, T>>)>;
+
+    // A list of key-value (string, T) tuples.
+    using KeyEntryVector = std::vector<std::pair<std::string, T>>;
+  };
+
+  // All blocking calls/disk access will happen on the provided |task_runner|.
+  ProtoLevelDBWrapper(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+
+  virtual ~ProtoLevelDBWrapper();
+
+  template <typename T>
+  void UpdateEntries(
+      std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+          entries_to_save,
+      std::unique_ptr<KeyVector> keys_to_remove,
+      UpdateCallback callback);
+
+  template <typename T>
+  void UpdateEntriesWithRemoveFilter(
+      std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+          entries_to_save,
+      const LevelDB::KeyFilter& delete_key_filter,
+      UpdateCallback callback);
+
+  template <typename T>
+  void LoadEntries(
+      typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback);
+
+  template <typename T>
+  void LoadEntriesWithFilter(
+      const LevelDB::KeyFilter& key_filter,
+      typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback);
+
+  template <typename T>
+  void LoadEntriesWithFilter(
+      const LevelDB::KeyFilter& key_filter,
+      const leveldb::ReadOptions& options,
+      const std::string& target_prefix,
+      typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback);
+
+  template <typename T>
+  void LoadKeysAndEntries(
+      typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+          callback);
+
+  template <typename T>
+  void LoadKeysAndEntriesWithFilter(
+      const LevelDB::KeyFilter& filter,
+      typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+          callback);
+
+  template <typename T>
+  void LoadKeysAndEntriesWithFilter(
+      const LevelDB::KeyFilter& filter,
+      const leveldb::ReadOptions& options,
+      const std::string& target_prefix,
+      typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+          callback);
+
+  void LoadKeys(LoadKeysCallback callback);
+
+  template <typename T>
+  void GetEntry(
+      const std::string& key,
+      typename ProtoLevelDBWrapper::Internal<T>::GetCallback callback);
+
+  void Destroy(DestroyCallback callback);
+
+  // Allow callers to provide their own Database implementation.
+  void InitWithDatabase(LevelDB* database,
+                        const base::FilePath& database_dir,
+                        const leveldb_env::Options& options,
+                        InitCallback callback);
+
+  bool GetApproximateMemoryUse(uint64_t* approx_mem_use);
+  const scoped_refptr<base::SequencedTaskRunner>& task_runner();
+
+ private:
+  THREAD_CHECKER(thread_checker_);
+
+  // Used to run blocking tasks in-order, must be the TaskRunner that |db_|
+  // relies on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  LevelDB* db_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtoLevelDBWrapper);
+};
+
+namespace {
+
+template <typename T>
+void RunUpdateCallback(typename ProtoLevelDBWrapper::UpdateCallback callback,
+                       const bool* success) {
+  std::move(callback).Run(*success);
+}
+
+template <typename T>
+void RunLoadCallback(
+    typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback,
+    bool* success,
+    std::unique_ptr<std::vector<T>> entries) {
+  std::move(callback).Run(*success, std::move(entries));
+}
+
+template <typename T>
+void RunLoadKeysAndEntriesCallback(
+    typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+        callback,
+    bool* success,
+    std::unique_ptr<std::map<std::string, T>> keys_entries) {
+  std::move(callback).Run(*success, std::move(keys_entries));
+}
+
+template <typename T>
+void RunGetCallback(
+    typename ProtoLevelDBWrapper::Internal<T>::GetCallback callback,
+    const bool* success,
+    const bool* found,
+    std::unique_ptr<T> entry) {
+  std::move(callback).Run(*success, *found ? std::move(entry) : nullptr);
+}
+
+template <typename T>
+void UpdateEntriesFromTaskRunner(
+    LevelDB* database,
+    std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+        entries_to_save,
+    std::unique_ptr<KeyVector> keys_to_remove,
+    bool* success) {
+  DCHECK(success);
+
+  // Serialize the values from Proto to string before passing on to database.
+  KeyValueVector pairs_to_save;
+  for (const auto& pair : *entries_to_save) {
+    pairs_to_save.push_back(
+        std::make_pair(pair.first, pair.second.SerializeAsString()));
+  }
+
+  *success = database->Save(pairs_to_save, *keys_to_remove);
+}
+
+template <typename T>
+void UpdateEntriesWithRemoveFilterFromTaskRunner(
+    LevelDB* database,
+    std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+        entries_to_save,
+    const LevelDB::KeyFilter& delete_key_filter,
+    bool* success) {
+  DCHECK(success);
+
+  // Serialize the values from Proto to string before passing on to database.
+  KeyValueVector pairs_to_save;
+  for (const auto& pair : *entries_to_save) {
+    pairs_to_save.push_back(
+        std::make_pair(pair.first, pair.second.SerializeAsString()));
+  }
+
+  *success = database->UpdateWithRemoveFilter(pairs_to_save, delete_key_filter);
+}
+
+template <typename T>
+void LoadKeysAndEntriesFromTaskRunner(LevelDB* database,
+                                      const LevelDB::KeyFilter& filter,
+                                      const leveldb::ReadOptions& options,
+                                      const std::string& target_prefix,
+                                      std::map<std::string, T>* keys_entries,
+                                      bool* success) {
+  DCHECK(success);
+  DCHECK(keys_entries);
+
+  keys_entries->clear();
+
+  std::map<std::string, std::string> loaded_entries;
+  *success = database->LoadKeysAndEntriesWithFilter(filter, &loaded_entries,
+                                                    options, target_prefix);
+
+  for (const auto& pair : loaded_entries) {
+    T entry;
+    if (!entry.ParseFromString(pair.second)) {
+      DLOG(WARNING) << "Unable to parse leveldb_proto entry";
+      // TODO(cjhopman): Decide what to do about un-parseable entries.
+    }
+
+    keys_entries->insert(std::make_pair(pair.first, entry));
+  }
+}
+
+template <typename T>
+void LoadEntriesFromTaskRunner(LevelDB* database,
+                               const LevelDB::KeyFilter& filter,
+                               const leveldb::ReadOptions& options,
+                               const std::string& target_prefix,
+                               std::vector<T>* entries,
+                               bool* success) {
+  DCHECK(success);
+  DCHECK(entries);
+
+  entries->clear();
+
+  std::vector<std::string> loaded_entries;
+  *success =
+      database->LoadWithFilter(filter, &loaded_entries, options, target_prefix);
+
+  for (const auto& serialized_entry : loaded_entries) {
+    T entry;
+    if (!entry.ParseFromString(serialized_entry)) {
+      DLOG(WARNING) << "Unable to parse leveldb_proto entry";
+      // TODO(cjhopman): Decide what to do about un-parseable entries.
+    }
+
+    entries->push_back(entry);
+  }
+}
+
+template <typename T>
+void GetEntryFromTaskRunner(LevelDB* database,
+                            const std::string& key,
+                            T* entry,
+                            bool* found,
+                            bool* success) {
+  DCHECK(success);
+  DCHECK(found);
+  DCHECK(entry);
+
+  std::string serialized_entry;
+  *success = database->Get(key, found, &serialized_entry);
+
+  if (!*success) {
+    *found = false;
+    return;
+  }
+
+  if (!*found)
+    return;
+
+  if (!entry->ParseFromString(serialized_entry)) {
+    *found = false;
+    DLOG(WARNING) << "Unable to parse leveldb_proto entry";
+    // TODO(cjhopman): Decide what to do about un-parseable entries.
+  }
+}
+
+}  // namespace
+
+template <typename T>
+void ProtoLevelDBWrapper::UpdateEntries(
+    std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+        entries_to_save,
+    std::unique_ptr<KeyVector> keys_to_remove,
+    typename ProtoLevelDBWrapper::UpdateCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bool* success = new bool(false);
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(UpdateEntriesFromTaskRunner<T>, base::Unretained(db_),
+                     std::move(entries_to_save), std::move(keys_to_remove),
+                     success),
+      base::BindOnce(RunUpdateCallback<T>, std::move(callback),
+                     base::Owned(success)));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::UpdateEntriesWithRemoveFilter(
+    std::unique_ptr<typename ProtoLevelDBWrapper::Internal<T>::KeyEntryVector>
+        entries_to_save,
+    const LevelDB::KeyFilter& delete_key_filter,
+    typename ProtoLevelDBWrapper::UpdateCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bool* success = new bool(false);
+
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner<T>,
+                     base::Unretained(db_), std::move(entries_to_save),
+                     delete_key_filter, success),
+      base::BindOnce(RunUpdateCallback<T>, std::move(callback),
+                     base::Owned(success)));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadEntries(
+    typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback) {
+  LoadEntriesWithFilter<T>(LevelDB::KeyFilter(), std::move(callback));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadEntriesWithFilter(
+    const LevelDB::KeyFilter& key_filter,
+    typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback) {
+  LoadEntriesWithFilter<T>(key_filter, leveldb::ReadOptions(), std::string(),
+                           std::move(callback));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadEntriesWithFilter(
+    const LevelDB::KeyFilter& key_filter,
+    const leveldb::ReadOptions& options,
+    const std::string& target_prefix,
+    typename ProtoLevelDBWrapper::Internal<T>::LoadCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bool* success = new bool(false);
+
+  std::unique_ptr<std::vector<T>> entries(new std::vector<T>());
+  // Get this pointer before entries is std::move()'d so we can use it below.
+  std::vector<T>* entries_ptr = entries.get();
+
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(LoadEntriesFromTaskRunner<T>, base::Unretained(db_),
+                     key_filter, options, target_prefix, entries_ptr, success),
+      base::BindOnce(RunLoadCallback<T>, std::move(callback),
+                     base::Owned(success), std::move(entries)));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadKeysAndEntries(
+    typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+        callback) {
+  LoadKeysAndEntriesWithFilter<T>(LevelDB::KeyFilter(), std::move(callback));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
+    const LevelDB::KeyFilter& key_filter,
+    typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+        callback) {
+  LoadKeysAndEntriesWithFilter<T>(key_filter, leveldb::ReadOptions(),
+                                  std::string(), std::move(callback));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
+    const LevelDB::KeyFilter& key_filter,
+    const leveldb::ReadOptions& options,
+    const std::string& target_prefix,
+    typename ProtoLevelDBWrapper::Internal<T>::LoadKeysAndEntriesCallback
+        callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bool* success = new bool(false);
+
+  auto keys_entries = std::make_unique<std::map<std::string, T>>();
+  // Get this pointer before entries is std::move()'d so we can use it below.
+  std::map<std::string, T>* keys_entries_ptr = keys_entries.get();
+
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(LoadKeysAndEntriesFromTaskRunner<T>, base::Unretained(db_),
+                     key_filter, options, target_prefix, keys_entries_ptr,
+                     success),
+      base::BindOnce(RunLoadKeysAndEntriesCallback<T>, std::move(callback),
+                     base::Owned(success), std::move(keys_entries)));
+}
+
+template <typename T>
+void ProtoLevelDBWrapper::GetEntry(
+    const std::string& key,
+    typename ProtoLevelDBWrapper::Internal<T>::GetCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bool* success = new bool(false);
+  bool* found = new bool(false);
+
+  std::unique_ptr<T> entry(new T());
+  // Get this pointer before entry is std::move()'d so we can use it below.
+  T* entry_ptr = entry.get();
+
+  task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(GetEntryFromTaskRunner<T>, base::Unretained(db_), key,
+                     entry_ptr, found, success),
+      base::BindOnce(RunGetCallback<T>, std::move(callback),
+                     base::Owned(success), base::Owned(found),
+                     std::move(entry)));
+}
+
+}  // namespace leveldb_proto
+
+#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/testing/fake_db.h b/components/leveldb_proto/testing/fake_db.h
index 56ed635..5753744 100644
--- a/components/leveldb_proto/testing/fake_db.h
+++ b/components/leveldb_proto/testing/fake_db.h
@@ -14,6 +14,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "base/task/post_task.h"
+#include "base/test/test_simple_task_runner.h"
 #include "components/leveldb_proto/proto_database.h"
 
 namespace leveldb_proto {
@@ -34,6 +36,11 @@
             const base::FilePath& database_dir,
             const leveldb_env::Options& options,
             typename ProtoDatabase<T>::InitCallback callback) override;
+  void InitWithDatabase(
+      LevelDB* database,
+      const base::FilePath& database_dir,
+      const leveldb_env::Options& options,
+      typename ProtoLevelDBWrapper::InitCallback callback) override;
   void UpdateEntries(
       std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
           entries_to_save,
@@ -115,7 +122,9 @@
 
 template <typename T>
 FakeDB<T>::FakeDB(EntryMap* db)
-    : db_(db) {}
+    : ProtoDatabase<T>(base::MakeRefCounted<base::TestSimpleTaskRunner>()) {
+  db_ = db;
+}
 
 template <typename T>
 FakeDB<T>::~FakeDB() {}
@@ -130,6 +139,15 @@
 }
 
 template <typename T>
+void FakeDB<T>::InitWithDatabase(
+    LevelDB* database,
+    const base::FilePath& database_dir,
+    const leveldb_env::Options& options,
+    typename ProtoLevelDBWrapper::InitCallback callback) {
+  Init("", database_dir, options, std::move(callback));
+}
+
+template <typename T>
 void FakeDB<T>::UpdateEntries(
     std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
     std::unique_ptr<std::vector<std::string>> keys_to_remove,
diff --git a/components/leveldb_proto/unique_proto_database.h b/components/leveldb_proto/unique_proto_database.h
new file mode 100644
index 0000000..3b3fc0f
--- /dev/null
+++ b/components/leveldb_proto/unique_proto_database.h
@@ -0,0 +1,51 @@
+// 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 COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
+#define COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
+
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "components/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+
+namespace leveldb_proto {
+
+// An implementation of ProtoDatabase<T> that manages the lifecycle of a unique
+// LevelDB instance.
+template <typename T>
+class UniqueProtoDatabase : public ProtoDatabase<T> {
+ public:
+  UniqueProtoDatabase(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+      : ProtoDatabase<T>(task_runner) {}
+
+  virtual void Init(const char* client_name,
+                    const base::FilePath& database_dir,
+                    const leveldb_env::Options& options,
+                    typename ProtoDatabase<T>::InitCallback callback) override {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    db_ = std::make_unique<LevelDB>(client_name);
+    ProtoDatabase<T>::InitWithDatabase(db_.get(), database_dir, options,
+                                       std::move(callback));
+  }
+
+  virtual ~UniqueProtoDatabase() {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    if (db_.get() &&
+        !this->db_wrapper_->task_runner()->DeleteSoon(FROM_HERE, db_.release()))
+      DLOG(WARNING) << "Proto database will not be deleted.";
+  }
+
+ private:
+  THREAD_CHECKER(thread_checker_);
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  std::unique_ptr<LevelDB> db_;
+};
+
+}  // namespace leveldb_proto
+
+#endif  // COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
diff --git a/components/leveldb_proto/proto_database_impl_unittest.cc b/components/leveldb_proto/unique_proto_database_unittest.cc
similarity index 84%
rename from components/leveldb_proto/proto_database_impl_unittest.cc
rename to components/leveldb_proto/unique_proto_database_unittest.cc
index c1053b09..721a5ec 100644
--- a/components/leveldb_proto/proto_database_impl_unittest.cc
+++ b/components/leveldb_proto/unique_proto_database_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 "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/unique_proto_database.h"
 
 #include <stddef.h>
 
@@ -174,13 +174,13 @@
   }
 }
 
-class ProtoDatabaseImplTest : public testing::Test {
+class UniqueProtoDatabaseTest : public testing::Test {
  public:
-  ProtoDatabaseImplTest()
+  UniqueProtoDatabaseTest()
       : options_(MakeMatcher(new OptionsEqMatcher(CreateSimpleOptions()))) {}
   void SetUp() override {
     main_loop_.reset(new MessageLoop());
-    db_.reset(new ProtoDatabaseImpl<TestProto>(main_loop_->task_runner()));
+    db_.reset(new UniqueProtoDatabase<TestProto>(main_loop_->task_runner()));
   }
 
   void TearDown() override {
@@ -190,32 +190,32 @@
   }
 
   const Matcher<const Options&> options_;
-  std::unique_ptr<ProtoDatabaseImpl<TestProto>> db_;
+  std::unique_ptr<UniqueProtoDatabase<TestProto>> db_;
   std::unique_ptr<MessageLoop> main_loop_;
 };
 
-// Test that ProtoDatabaseImpl calls Init on the underlying database and that
+// Test that UniqueProtoDatabase calls Init on the underlying database and that
 // the caller's InitCallback is called with the correct value.
-TEST_F(ProtoDatabaseImplTest, TestDBInitSuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBInitSuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
 
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBInitFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBInitFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   Options options;
   options.create_if_missing = true;
   EXPECT_CALL(*mock_db, Init(path, OptionsEq(options))).WillOnce(Return(false));
@@ -223,23 +223,23 @@
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(false));
 
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, options,
+  db_->InitWithDatabase(mock_db.get(), path, options,
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBDestroySuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBDestroySuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
 
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -251,16 +251,16 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBDestroyFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBDestroyFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   EXPECT_CALL(*mock_db, Init(path, options_)).WillOnce(Return(true));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(true));
 
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -301,47 +301,47 @@
   ExpectEntryPointersEquals(expected, actual);
 }
 
-// Test that ProtoDatabaseImpl calls Load on the underlying database and that
+// Test that UniqueProtoDatabase calls Load on the underlying database and that
 // the caller's LoadCallback is called with the correct success value. Also
 // confirms that on success, the expected entries are passed to the caller's
 // LoadCallback.
-TEST_F(ProtoDatabaseImplTest, TestDBLoadSuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBLoadSuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
   EXPECT_CALL(*mock_db, LoadKeysAndEntriesWithFilter(_, _, _, _))
       .WillOnce(AppendLoadKeysAndEntries(model));
-  EXPECT_CALL(caller, LoadCallback1(true, _))
-      .WillOnce(VerifyLoadEntries(testing::ByRef(model)));
-  db_->LoadEntries(base::BindOnce(&MockDatabaseCaller::LoadCallback,
-                                  base::Unretained(&caller)));
+  EXPECT_CALL(caller, LoadKeysAndEntriesCallback1(true, _))
+      .WillOnce(VerifyLoadKeysAndEntries(testing::ByRef(model)));
+  db_->LoadKeysAndEntries(
+      base::BindOnce(&MockDatabaseCaller::LoadKeysAndEntriesCallback,
+                     base::Unretained(&caller)));
 
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBLoadFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBLoadFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
-  EXPECT_CALL(*mock_db, LoadKeysAndEntriesWithFilter(_, _, _, _))
-      .WillOnce(Return(false));
+  EXPECT_CALL(*mock_db, LoadWithFilter(_, _, _, _)).WillOnce(Return(false));
   EXPECT_CALL(caller, LoadCallback1(false, _));
   db_->LoadEntries(base::BindOnce(&MockDatabaseCaller::LoadCallback,
                                   base::Unretained(&caller)));
@@ -368,16 +368,16 @@
   EXPECT_EQ(expected.SerializeAsString(), actual->SerializeAsString());
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBGetSuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBGetSuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -392,7 +392,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-class ProtoDatabaseImplLevelDBTest : public testing::Test {
+class UniqueProtoDatabaseLevelDBTest : public testing::Test {
  public:
   void SetUp() override { main_loop_.reset(new MessageLoop()); }
 
@@ -405,45 +405,46 @@
   std::unique_ptr<MessageLoop> main_loop_;
 };
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBSaveAndLoadKeys) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBSaveAndLoadKeys) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::Thread db_thread("dbthread");
   ASSERT_TRUE(db_thread.Start());
-  std::unique_ptr<ProtoDatabaseImpl<TestProto>> db(
-      new ProtoDatabaseImpl<TestProto>(db_thread.task_runner()));
+  std::unique_ptr<UniqueProtoDatabase<TestProto>> db(
+      new UniqueProtoDatabase<TestProto>(db_thread.task_runner()));
 
   auto expect_init_success =
-      base::Bind([](bool success) { EXPECT_TRUE(success); });
+      base::BindOnce([](bool success) { EXPECT_TRUE(success); });
   db->Init(kTestLevelDBClientName, temp_dir.GetPath(), CreateSimpleOptions(),
-           expect_init_success);
+           std::move(expect_init_success));
 
   base::RunLoop run_update_entries;
-  auto expect_update_success = base::Bind(
-      [](base::Closure signal, bool success) {
+  auto expect_update_success = base::BindOnce(
+      [](base::OnceClosure signal, bool success) {
         EXPECT_TRUE(success);
-        signal.Run();
+        std::move(signal).Run();
       },
       run_update_entries.QuitClosure());
   TestProto test_proto;
   test_proto.set_data("some data");
   ProtoDatabase<TestProto>::KeyEntryVector data_set(
-          {{"0", test_proto}, {"1", test_proto}, {"2", test_proto}});
+      {{"0", test_proto}, {"1", test_proto}, {"2", test_proto}});
   db->UpdateEntries(
       std::make_unique<ProtoDatabase<TestProto>::KeyEntryVector>(data_set),
-      std::make_unique<std::vector<std::string>>(), expect_update_success);
+      std::make_unique<std::vector<std::string>>(),
+      std::move(expect_update_success));
   run_update_entries.Run();
 
   base::RunLoop run_load_keys;
-  auto verify_loaded_keys = base::Bind(
-      [](base::Closure signal, bool success,
+  auto verify_loaded_keys = base::BindOnce(
+      [](base::OnceClosure signal, bool success,
          std::unique_ptr<std::vector<std::string>> keys) {
         EXPECT_TRUE(success);
         EXPECT_THAT(*keys, UnorderedElementsAre("0", "1", "2"));
-        signal.Run();
+        std::move(signal).Run();
       },
       run_load_keys.QuitClosure());
-  db->LoadKeys(verify_loaded_keys);
+  db->LoadKeys(std::move(verify_loaded_keys));
   run_load_keys.Run();
 
   // Shutdown database.
@@ -454,16 +455,16 @@
   run_destruction.Run();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBGetNotFound) {
+TEST_F(UniqueProtoDatabaseTest, TestDBGetNotFound) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -477,16 +478,16 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBGetFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBGetFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -519,19 +520,19 @@
   return true;
 }
 
-// Test that ProtoDatabaseImpl calls Save on the underlying database with the
+// Test that UniqueProtoDatabase calls Save on the underlying database with the
 // correct entries to save and that the caller's SaveCallback is called with the
 // correct success value.
-TEST_F(ProtoDatabaseImplTest, TestDBSaveSuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBSaveSuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -551,10 +552,10 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBSaveFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBSaveFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   std::unique_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
       new ProtoDatabase<TestProto>::KeyEntryVector());
@@ -562,7 +563,7 @@
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -575,19 +576,19 @@
   base::RunLoop().RunUntilIdle();
 }
 
-// Test that ProtoDatabaseImpl calls Save on the underlying database with the
+// Test that UniqueProtoDatabase calls Save on the underlying database with the
 // correct entries to delete and that the caller's SaveCallback is called with
 // the correct success value.
-TEST_F(ProtoDatabaseImplTest, TestDBRemoveSuccess) {
+TEST_F(UniqueProtoDatabaseTest, TestDBRemoveSuccess) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   EntryMap model = GetSmallModel();
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -607,10 +608,10 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ProtoDatabaseImplTest, TestDBRemoveFailure) {
+TEST_F(UniqueProtoDatabaseTest, TestDBRemoveFailure) {
   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
 
-  MockDB* mock_db = new MockDB();
+  auto mock_db = std::make_unique<MockDB>();
   MockDatabaseCaller caller;
   std::unique_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
       new ProtoDatabase<TestProto>::KeyEntryVector());
@@ -618,7 +619,7 @@
 
   EXPECT_CALL(*mock_db, Init(_, options_));
   EXPECT_CALL(caller, InitCallback(_));
-  db_->InitWithDatabase(base::WrapUnique(mock_db), path, CreateSimpleOptions(),
+  db_->InitWithDatabase(mock_db.get(), path, CreateSimpleOptions(),
                         base::BindOnce(&MockDatabaseCaller::InitCallback,
                                        base::Unretained(&caller)));
 
@@ -633,7 +634,7 @@
 
 // This tests that normal usage of the real database does not cause any
 // threading violations.
-TEST(ProtoDatabaseImplThreadingTest, TestDBDestruction) {
+TEST(UniqueProtoDatabaseThreadingTest, TestDBDestruction) {
   base::MessageLoop main_loop;
 
   ScopedTempDir temp_dir;
@@ -642,8 +643,8 @@
   base::Thread db_thread("dbthread");
   ASSERT_TRUE(db_thread.Start());
 
-  std::unique_ptr<ProtoDatabaseImpl<TestProto>> db(
-      new ProtoDatabaseImpl<TestProto>(db_thread.task_runner()));
+  std::unique_ptr<UniqueProtoDatabase<TestProto>> db(
+      new UniqueProtoDatabase<TestProto>(db_thread.task_runner()));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(_));
@@ -661,7 +662,7 @@
 
 // This tests that normal usage of the real database does not cause any
 // threading violations.
-TEST(ProtoDatabaseImplThreadingTest, TestDBDestroy) {
+TEST(UniqueProtoDatabaseThreadingTest, TestDBDestroy) {
   base::MessageLoop main_loop;
 
   ScopedTempDir temp_dir;
@@ -670,8 +671,8 @@
   base::Thread db_thread("dbthread");
   ASSERT_TRUE(db_thread.Start());
 
-  std::unique_ptr<ProtoDatabaseImpl<TestProto>> db(
-      new ProtoDatabaseImpl<TestProto>(db_thread.task_runner()));
+  std::unique_ptr<UniqueProtoDatabase<TestProto>> db(
+      new UniqueProtoDatabase<TestProto>(db_thread.task_runner()));
 
   MockDatabaseCaller caller;
   EXPECT_CALL(caller, InitCallback(_));
@@ -734,15 +735,15 @@
   ExpectEntryPointersEquals(model, loaded_protos);
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBSaveAndLoad) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBSaveAndLoad) {
   TestLevelDBSaveAndLoad(false);
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBCloseAndReopen) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBCloseAndReopen) {
   TestLevelDBSaveAndLoad(true);
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBLoadWithFilter) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBLoadWithFilter) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
@@ -770,7 +771,7 @@
   EXPECT_EQ(entry.SerializeAsString(), model["0"].SerializeAsString());
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBLoadKeysAndEntries) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBLoadKeysAndEntries) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
@@ -799,7 +800,7 @@
   }
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBInitFail) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBInitFail) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
@@ -816,7 +817,7 @@
   EXPECT_FALSE(db->Save(save_entries, remove_keys));
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestMemoryDatabase) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestMemoryDatabase) {
   std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
 
   std::vector<std::string> load_entries;
@@ -837,7 +838,7 @@
   EXPECT_EQ(1u, second_load_entries.size());
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestCorruptDBReset) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestCorruptDBReset) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
@@ -867,7 +868,7 @@
   ASSERT_FALSE(found);
 }
 
-TEST_F(ProtoDatabaseImplLevelDBTest, TestDBDeleteWithFilter) {
+TEST_F(UniqueProtoDatabaseLevelDBTest, TestDBDeleteWithFilter) {
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 71d1ae47..09696c5 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -28,6 +28,7 @@
     "answer_sunrise.icon",
     "answer_when_is.icon",
     "drive_docs.icon",
+    "drive_forms.icon",
     "drive_logo.icon",
     "drive_sheets.icon",
     "drive_slides.icon",
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index c671b07..c989a3a 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -248,6 +248,8 @@
       switch (document_type) {
         case DocumentType::DRIVE_DOCS:
           return omnibox::kDriveDocsIcon;
+        case DocumentType::DRIVE_FORMS:
+          return omnibox::kDriveFormsIcon;
         case DocumentType::DRIVE_SHEETS:
           return omnibox::kDriveSheetsIcon;
         case DocumentType::DRIVE_SLIDES:
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index a2c987d..65946ace 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -117,6 +117,7 @@
   enum class DocumentType {
     NONE,
     DRIVE_DOCS,
+    DRIVE_FORMS,
     DRIVE_SHEETS,
     DRIVE_SLIDES,
     DRIVE_OTHER
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index 11c14e74..66aa66e 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -58,6 +58,7 @@
 
 // MIME types sent by the server for different document types.
 const char kDocumentMimetype[] = "application/vnd.google-apps.document";
+const char kFormMimetype[] = "application/vnd.google-apps.form";
 const char kSpreadsheetMimetype[] = "application/vnd.google-apps.spreadsheet";
 const char kPresentationMimetype[] = "application/vnd.google-apps.presentation";
 
@@ -332,6 +333,8 @@
 base::string16 GetProductDescriptionString(const std::string& mimetype) {
   if (mimetype == kDocumentMimetype)
     return l10n_util::GetStringUTF16(IDS_DRIVE_SUGGESTION_DOCUMENT);
+  if (mimetype == kFormMimetype)
+    return l10n_util::GetStringUTF16(IDS_DRIVE_SUGGESTION_FORM);
   if (mimetype == kSpreadsheetMimetype)
     return l10n_util::GetStringUTF16(IDS_DRIVE_SUGGESTION_SPREADSHEET);
   if (mimetype == kPresentationMimetype)
@@ -426,6 +429,8 @@
       if (metadata->GetString("mimeType", &mimetype)) {
         if (mimetype == kDocumentMimetype) {
           match.document_type = AutocompleteMatch::DocumentType::DRIVE_DOCS;
+        } else if (mimetype == kFormMimetype) {
+          match.document_type = AutocompleteMatch::DocumentType::DRIVE_FORMS;
         } else if (mimetype == kSpreadsheetMimetype) {
           match.document_type = AutocompleteMatch::DocumentType::DRIVE_SHEETS;
         } else if (mimetype == kPresentationMimetype) {
diff --git a/components/omnibox/browser/vector_icons/drive_forms.icon b/components/omnibox/browser/vector_icons/drive_forms.icon
new file mode 100644
index 0000000..f174d9a
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/drive_forms.icon
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.

+// Use of this source code is governed by a BSD-style license that can be

+// found in the LICENSE file.

+

+CANVAS_DIMENSIONS, 18,

+PATH_COLOR_ARGB, 0xFF, 0x67, 0x3A, 0xB7,

+MOVE_TO, 16, 0,

+H_LINE_TO, 2,

+CUBIC_TO, 0.9f, 0, 0, 0.9f, 0, 2,

+R_V_LINE_TO, 14,

+R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2,

+R_H_LINE_TO, 14,

+R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2,

+V_LINE_TO, 2,

+R_CUBIC_TO, 0, -1.1f, -0.9f, -2, -2, -2,

+CLOSE,

+MOVE_TO, 6, 14,

+H_LINE_TO, 4,

+R_V_LINE_TO, -2,

+R_H_LINE_TO, 2,

+CLOSE,

+R_MOVE_TO, 0, -4,

+H_LINE_TO, 4,

+V_LINE_TO, 8,

+R_H_LINE_TO, 2,

+CLOSE,

+R_MOVE_TO, 0, -4,

+H_LINE_TO, 4,

+V_LINE_TO, 4,

+R_H_LINE_TO, 2,

+CLOSE,

+R_MOVE_TO, 8, 8,

+H_LINE_TO, 7,

+R_V_LINE_TO, -2,

+R_H_LINE_TO, 7,

+CLOSE,

+R_MOVE_TO, 0, -4,

+H_LINE_TO, 7,

+V_LINE_TO, 8,

+R_H_LINE_TO, 7,

+CLOSE,

+R_MOVE_TO, 0, -4,

+H_LINE_TO, 7,

+V_LINE_TO, 4,

+R_H_LINE_TO, 7,

+CLOSE
\ No newline at end of file
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 50c6ac4b..97a2b9ab 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -61,6 +61,9 @@
   <message name="IDS_DRIVE_SUGGESTION_DOCUMENT" desc="Google Docs product name, for use in omnibox Docs result descriptions.">
     Google Docs
   </message>
+  <message name="IDS_DRIVE_SUGGESTION_FORM" desc="Google Docs product name, for use in omnibox Form result descriptions.">
+    Google Forms
+  </message>
   <message name="IDS_DRIVE_SUGGESTION_SPREADSHEET" desc="Google Sheets product name, for use in omnibox Sheets result descriptions.">
     Google Sheets
   </message>
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index fe8f2cd7..310e771 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -401,12 +401,17 @@
     const PasswordManagerDriver* driver) {
   if (!DoesManage(submitted_form, driver))
     return false;
-  submitted_form_ = submitted_form;
-  is_submitted_ = true;
   parsed_submitted_form_ =
-      ParseFormAndMakeLogging(submitted_form_, FormDataParser::Mode::kSaving);
+      ParseFormAndMakeLogging(submitted_form, FormDataParser::Mode::kSaving);
+
   RecordMetricOnReadonly(parser_.readonly_status(), !!parsed_submitted_form_,
                          FormDataParser::Mode::kSaving);
+  if (!parsed_submitted_form_)
+    return false;
+
+  submitted_form_ = submitted_form;
+  is_submitted_ = true;
+
   CreatePendingCredentials();
   return true;
 }
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index 00f32a4..53b1544 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -314,10 +314,10 @@
 TEST_F(NewPasswordFormManagerTest, SetSubmitted) {
   EXPECT_FALSE(form_manager_->is_submitted());
   EXPECT_TRUE(
-      form_manager_->SetSubmittedFormIfIsManaged(observed_form_, &driver_));
+      form_manager_->SetSubmittedFormIfIsManaged(submitted_form_, &driver_));
   EXPECT_TRUE(form_manager_->is_submitted());
 
-  FormData another_form = observed_form_;
+  FormData another_form = submitted_form_;
   another_form.name += ASCIIToUTF16("1");
   // |another_form| is managed because the same |unique_renderer_id| as
   // |observed_form_|.
@@ -338,6 +338,14 @@
   EXPECT_FALSE(
       form_manager_->SetSubmittedFormIfIsManaged(observed_form_, nullptr));
   EXPECT_FALSE(form_manager_->is_submitted());
+
+  // Check if the subbmitted form can not be parsed then form manager does not
+  // became submitted.
+  FormData malformed_form = submitted_form_;
+  malformed_form.fields.clear();
+  EXPECT_FALSE(
+      form_manager_->SetSubmittedFormIfIsManaged(malformed_form, &driver_));
+  EXPECT_FALSE(form_manager_->is_submitted());
 }
 
 // Tests that when NewPasswordFormManager receives saved matches it waits for
@@ -937,9 +945,9 @@
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
   fetcher_->SetNonFederated({&saved_match_}, 0u);
 
-  FormData malformed_form = observed_form_;
+  FormData malformed_form = submitted_form_;
   malformed_form.fields.clear();
-  EXPECT_TRUE(
+  EXPECT_FALSE(
       form_manager_->SetSubmittedFormIfIsManaged(malformed_form, &driver_));
 
   // Destroy the form manager to destroy the UKM recorder it owns. The recorder
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index c9872b0..9cd0d02 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -89,6 +89,8 @@
 PasswordForm CreateMinimalCrowdsourcableForm(
     const PasswordForm& observed_form) {
   PasswordForm form = observed_form;
+  form.origin = GURL("https://www.foo.com/login");
+  form.form_data.origin = form.origin;
   autofill::FormFieldData field;
   field.name = ASCIIToUTF16("email");
   field.form_control_type = "text";
@@ -4514,6 +4516,9 @@
 
   // User submits credentials for the observed form.
   PasswordForm submitted_form = *observed_form();
+  submitted_form.origin = GURL("https://www.foo.com/login");
+  submitted_form.form_data.origin = submitted_form.origin;
+
   autofill::FormFieldData field;
   field.name = ASCIIToUTF16("password1");
   field.form_control_type = "password";
@@ -4603,6 +4608,8 @@
   observed_form()->password_element = ASCIIToUTF16("password");
   // User submits credentials for the observed form.
   PasswordForm submitted_form = *observed_form();
+  submitted_form.origin = GURL("https://www.foo.com/login");
+  submitted_form.form_data.origin = submitted_form.origin;
   submitted_form.username_value = saved_match()->username_value;
   submitted_form.password_value = saved_match()->password_value;
   submitted_form.form_data.fields[0].value = submitted_form.username_value;
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index d7db1ac..9d359ce2 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -13,6 +13,7 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
@@ -506,6 +507,7 @@
 
 void PasswordStore::GetLoginsImpl(const FormDigest& form,
                                   std::unique_ptr<GetLoginsRequest> request) {
+  SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.GetLogins");
   request->NotifyConsumerWithResults(FillMatchingLogins(form));
 }
 
@@ -648,16 +650,19 @@
 }
 
 void PasswordStore::AddLoginInternal(const PasswordForm& form) {
+  SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.AddLogin");
   PasswordStoreChangeList changes = AddLoginImpl(form);
   NotifyLoginsChanged(changes);
 }
 
 void PasswordStore::UpdateLoginInternal(const PasswordForm& form) {
+  SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.UpdateLogin");
   PasswordStoreChangeList changes = UpdateLoginImpl(form);
   NotifyLoginsChanged(changes);
 }
 
 void PasswordStore::RemoveLoginInternal(const PasswordForm& form) {
+  SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.RemoveLogin");
   PasswordStoreChangeList changes = RemoveLoginImpl(form);
   NotifyLoginsChanged(changes);
 }
@@ -844,6 +849,7 @@
 
 std::unique_ptr<PasswordForm> PasswordStore::GetLoginImpl(
     const PasswordForm& primary_key) {
+  SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.GetLogin");
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
   std::vector<std::unique_ptr<PasswordForm>> candidates(
       FillMatchingLogins(FormDigest(primary_key)));
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 4963a29..407ca26 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -671,6 +671,7 @@
       'policies': [
         'NetworkFileSharesAllowed',
         'NetBiosShareDiscoveryEnabled',
+        'NTLMShareAuthenticationEnabled',
       ],
     },
     {
@@ -13445,6 +13446,27 @@
 
       This policy is meant to be used together with the "Always on VPN" feature, that lets the admin decide to establish a VPN connection on boot.''',
     },
+    {
+      'id': 489,
+      'name': 'NTLMShareAuthenticationEnabled',
+      'type': 'main',
+      'schema': {'type': 'boolean', },
+      'tags': [],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': True,
+      },
+      'supported_on': ['chrome_os:71-'],
+      'caption': '''Controls enabling NTLM as an authentication protocol for SMB mounts''',
+      'example_value': True,
+      'default_for_enterprise_users': False,
+      'desc': '''This policy controls whether the Network File Shares feature for <ph name="PRODUCT_NAME">$2<ex>Google Chrome OS</ex></ph> will use NTLM for authentication.
+
+      When this policy is set to True, NTLM will be used for authentication to SMB shares if necessary.
+      When this policy is set to False, NTLM authentication to SMB shares will be disabled.
+
+      If the policy is left not set, the default is disabled for enterprise-managed users and enabled for non-managed users.''',
+    },
   ],
 
   'messages': {
@@ -13586,5 +13608,5 @@
   },
   'placeholders': [],
   'deleted_policy_ids': [412],
-  'highest_id_currently_used': 488
+  'highest_id_currently_used': 489
 }
diff --git a/components/previews/content/previews_content_util.cc b/components/previews/content/previews_content_util.cc
index 4bd2a65..31757b7 100644
--- a/components/previews/content/previews_content_util.cc
+++ b/components/previews/content/previews_content_util.cc
@@ -23,6 +23,9 @@
     previews::PreviewsDecider* previews_decider) {
   content::PreviewsState previews_state = content::PREVIEWS_UNSPECIFIED;
 
+  // Record whether the hint cache has a matching entry for this pre-commit URL.
+  previews_decider->LogHintCacheMatch(url, false /* is_committed */);
+
   if (!previews::params::ArePreviewsAllowed()) {
     return previews_state;
   }
@@ -75,6 +78,9 @@
     const previews::PreviewsDecider* previews_decider) {
   bool is_https = url.SchemeIs(url::kHttpsScheme);
 
+  // Record whether the hint cache has a matching entry for this committed URL.
+  previews_decider->LogHintCacheMatch(url, true /* is_committed */);
+
   // Check if an offline preview was actually served.
   if (previews_data && previews_data->offline_preview_used()) {
     DCHECK(previews_state & content::OFFLINE_PAGE_ON);
diff --git a/components/previews/content/previews_content_util_unittest.cc b/components/previews/content/previews_content_util_unittest.cc
index b6efc2b3..c2a8a82 100644
--- a/components/previews/content/previews_content_util_unittest.cc
+++ b/components/previews/content/previews_content_util_unittest.cc
@@ -55,6 +55,8 @@
 
   void LoadResourceHints(const GURL& url) override {}
 
+  void LogHintCacheMatch(const GURL& url, bool is_committed) const override {}
+
  private:
   bool IsEnabled(PreviewsType type) const {
     switch (type) {
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc
index 41826e2c..fc9c3d8e 100644
--- a/components/previews/content/previews_decider_impl.cc
+++ b/components/previews/content/previews_decider_impl.cc
@@ -406,6 +406,15 @@
                           weak_factory_.GetWeakPtr()));
 }
 
+void PreviewsDeciderImpl::LogHintCacheMatch(const GURL& url,
+                                            bool is_committed) const {
+  if (!previews_opt_guide_)
+    return;
+
+  previews_opt_guide_->LogHintCacheMatch(url, is_committed,
+                                         effective_connection_type_);
+}
+
 bool PreviewsDeciderImpl::IsURLAllowedForPreview(
     PreviewsUserData* previews_data,
     const GURL& url,
diff --git a/components/previews/content/previews_decider_impl.h b/components/previews/content/previews_decider_impl.h
index 16c3f0c..8e2dba02 100644
--- a/components/previews/content/previews_decider_impl.h
+++ b/components/previews/content/previews_decider_impl.h
@@ -131,6 +131,8 @@
 
   void LoadResourceHints(const GURL& url) override;
 
+  void LogHintCacheMatch(const GURL& url, bool is_committed) const override;
+
   // Generates a page ID that is guaranteed to be unique from any other page ID
   // generated in this browser session. Also, guaranteed to be non-zero.
   uint64_t GeneratePageId();
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index 4cd2ab55..41c353f 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -518,4 +518,35 @@
   return hint_cache_->HasHint(url.host());
 }
 
+void PreviewsHints::LogHintCacheMatch(const GURL& url,
+                                      bool is_committed,
+                                      net::EffectiveConnectionType ect) const {
+  if (!hint_cache_)
+    return;
+
+  if (hint_cache_->HasHint(url.host())) {
+    if (!is_committed) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "Previews.OptimizationGuide.HintCache.HasHint.BeforeCommit", ect,
+          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
+    } else {
+      UMA_HISTOGRAM_ENUMERATION(
+          "Previews.OptimizationGuide.HintCache.HasHint.AtCommit", ect,
+          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
+      if (hint_cache_->IsHintLoaded(url.host())) {
+        UMA_HISTOGRAM_ENUMERATION(
+            "Previews.OptimizationGuide.HintCache.HostMatch.AtCommit", ect,
+            net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
+        const optimization_guide::proto::Hint* hint =
+            hint_cache_->GetHint(url.host());
+        if (FindPageHint(url, *hint) != nullptr) {
+          UMA_HISTOGRAM_ENUMERATION(
+              "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit", ect,
+              net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
+        }
+      }
+    }
+  }
+}
+
 }  // namespace previews
diff --git a/components/previews/content/previews_hints.h b/components/previews/content/previews_hints.h
index 9ade6551..92165af 100644
--- a/components/previews/content/previews_hints.h
+++ b/components/previews/content/previews_hints.h
@@ -17,6 +17,7 @@
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/host_filter.h"
 #include "components/url_matcher/url_matcher.h"
+#include "net/nqe/effective_connection_type.h"
 
 class GURL;
 
@@ -65,6 +66,13 @@
   bool MaybeLoadOptimizationHints(const GURL& url,
                                   HintLoadedCallback callback) const;
 
+  // Logs UMA for whether the HintCache has a matching Hint and also a matching
+  // PageHint for |url|. Records the client's current |ect| as well. This is
+  // useful for measuring the effectiveness of the page hints provided by Cacao.
+  void LogHintCacheMatch(const GURL& url,
+                         bool is_committed,
+                         net::EffectiveConnectionType ect) const;
+
  private:
   friend class PreviewsHintsTest;
 
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc
index f7ca4b2..c86fe7a 100644
--- a/components/previews/content/previews_hints_unittest.cc
+++ b/components/previews/content/previews_hints_unittest.cc
@@ -105,6 +105,66 @@
                             GURL("https://www.foo.org/bar/three.jpg"), hint1));
 }
 
+TEST_F(PreviewsHintsTest, LogHintCacheMatch) {
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
+
+  optimization_guide::proto::Configuration config;
+  optimization_guide::proto::Hint* hint1 = config.add_hints();
+  hint1->set_key("somedomain.org");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
+  page_hint1->set_page_pattern("/news/");
+  optimization_guide::proto::Optimization* optimization1 =
+      page_hint1->add_whitelisted_optimizations();
+  optimization1->set_optimization_type(
+      optimization_guide::proto::RESOURCE_LOADING);
+  optimization_guide::proto::ResourceLoadingHint* resource_loading_hint1 =
+      optimization1->add_resource_loading_hints();
+  resource_loading_hint1->set_loading_optimization_type(
+      optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+  resource_loading_hint1->set_resource_pattern("news_cruft.js");
+  ParseConfig(config);
+
+  base::HistogramTester histogram_tester;
+
+  // First verify no histogram counts for non-matching URL host.
+  previews_hints()->LogHintCacheMatch(
+      GURL("https://someotherdomain.com/news/story.html"),
+      false /* is_committed */, net::EFFECTIVE_CONNECTION_TYPE_3G);
+  previews_hints()->LogHintCacheMatch(
+      GURL("https://someotherdomain.com/news/story2.html"),
+      true /* is_committed */, net::EFFECTIVE_CONNECTION_TYPE_4G);
+  histogram_tester.ExpectTotalCount(
+      "Previews.OptimizationGuide.HintCache.HasHint.BeforeCommit", 0);
+  histogram_tester.ExpectTotalCount(
+      "Previews.OptimizationGuide.HintCache.HasHint.AtCommit", 0);
+  histogram_tester.ExpectTotalCount(
+      "Previews.OptimizationGuide.HintCache.HintLoaded.AtCommit", 0);
+  histogram_tester.ExpectTotalCount(
+      "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit", 0);
+
+  // Now verify do have histogram counts for matching URL host.
+  previews_hints()->LogHintCacheMatch(
+      GURL("https://somedomain.org/news/story.html"), false /* is_committed */,
+      net::EFFECTIVE_CONNECTION_TYPE_3G);
+  previews_hints()->LogHintCacheMatch(
+      GURL("https://somedomain.org/news/story2.html"), true /* is_committed */,
+      net::EFFECTIVE_CONNECTION_TYPE_4G);
+  histogram_tester.ExpectBucketCount(
+      "Previews.OptimizationGuide.HintCache.HasHint.BeforeCommit",
+      4 /* EFFECTIVE_CONNECTION_TYPE_3G */, 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.OptimizationGuide.HintCache.HasHint.AtCommit",
+      5 /* EFFECTIVE_CONNECTION_TYPE_4G */, 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.OptimizationGuide.HintCache.HostMatch.AtCommit",
+      5 /* EFFECTIVE_CONNECTION_TYPE_4G */, 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit",
+      5 /* EFFECTIVE_CONNECTION_TYPE_4G */, 1);
+}
+
 TEST_F(PreviewsHintsTest, IsBlacklisted) {
   std::unique_ptr<PreviewsHints> previews_hints =
       PreviewsHints::CreateForTesting(
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index ba8e2d9..7fb4810 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -104,6 +104,16 @@
                           std::move(callback), url));
 }
 
+void PreviewsOptimizationGuide::LogHintCacheMatch(
+    const GURL& url,
+    bool is_committed,
+    net::EffectiveConnectionType ect) const {
+  if (!hints_)
+    return;
+
+  hints_->LogHintCacheMatch(url, is_committed, ect);
+}
+
 void PreviewsOptimizationGuide::OnHintsProcessed(
     const optimization_guide::proto::Configuration& config,
     const optimization_guide::ComponentInfo& info) {
diff --git a/components/previews/content/previews_optimization_guide.h b/components/previews/content/previews_optimization_guide.h
index fe988ef..9603f03d 100644
--- a/components/previews/content/previews_optimization_guide.h
+++ b/components/previews/content/previews_optimization_guide.h
@@ -64,6 +64,13 @@
   bool MaybeLoadOptimizationHints(const GURL& url,
                                   ResourceLoadingHintsCallback callback);
 
+  // Logs UMA for whether the OptimizationGuide HintCache has a matching Hint
+  // guidance for |url|. This is useful for measuring the effectiveness of the
+  // page hints provided by Cacao.
+  void LogHintCacheMatch(const GURL& url,
+                         bool is_committed,
+                         net::EffectiveConnectionType ect) const;
+
   // optimization_guide::OptimizationGuideServiceObserver implementation:
   void OnHintsProcessed(
       const optimization_guide::proto::Configuration& config,
diff --git a/components/previews/core/previews_decider.h b/components/previews/core/previews_decider.h
index 2e89162c..a721654 100644
--- a/components/previews/core/previews_decider.h
+++ b/components/previews/core/previews_decider.h
@@ -53,6 +53,11 @@
   // Requests that any applicable detailed resource hints be loaded.
   virtual void LoadResourceHints(const GURL& url) = 0;
 
+  // Logs UMA for whether the OptimizationGuide HintCache has a matching Hint
+  // guidance for |url|. This is useful for measuring the effectiveness of the
+  // page hints provided by Cacao.
+  virtual void LogHintCacheMatch(const GURL& url, bool is_committed) const = 0;
+
  protected:
   PreviewsDecider() {}
   virtual ~PreviewsDecider() {}
diff --git a/components/previews/core/test_previews_decider.cc b/components/previews/core/test_previews_decider.cc
index 09e41b0d..a6f31b328 100644
--- a/components/previews/core/test_previews_decider.cc
+++ b/components/previews/core/test_previews_decider.cc
@@ -38,4 +38,7 @@
 
 void TestPreviewsDecider::LoadResourceHints(const GURL& url) {}
 
+void TestPreviewsDecider::LogHintCacheMatch(const GURL& url,
+                                            bool is_committed) const {}
+
 }  // namespace previews
diff --git a/components/previews/core/test_previews_decider.h b/components/previews/core/test_previews_decider.h
index 77dc8fb..577829d8 100644
--- a/components/previews/core/test_previews_decider.h
+++ b/components/previews/core/test_previews_decider.h
@@ -32,6 +32,7 @@
                               const GURL& url,
                               PreviewsType type) const override;
   void LoadResourceHints(const GURL& url) override;
+  void LogHintCacheMatch(const GURL& url, bool is_committed) const override;
 
  private:
   bool allow_previews_;
diff --git a/components/safe_browsing/common/safe_browsing_prefs.cc b/components/safe_browsing/common/safe_browsing_prefs.cc
index c300dd0..97ffdb9 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/common/safe_browsing_prefs.cc
@@ -169,28 +169,23 @@
                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool ExtendedReportingPrefExists(const PrefService& prefs) {
-  return prefs.HasPrefPath(GetExtendedReportingPrefName(prefs));
+  return prefs.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled);
 }
 
 ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs) {
   return IsExtendedReportingEnabled(prefs) ? SBER_LEVEL_SCOUT : SBER_LEVEL_OFF;
 }
 
-const char* GetExtendedReportingPrefName(const PrefService& prefs) {
-  // TODO(lpz): Remove this method, use the pref directly in calling code.
-  return prefs::kSafeBrowsingScoutReportingEnabled;
-}
-
 bool IsExtendedReportingOptInAllowed(const PrefService& prefs) {
   return prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
 }
 
 bool IsExtendedReportingEnabled(const PrefService& prefs) {
-  return prefs.GetBoolean(GetExtendedReportingPrefName(prefs));
+  return prefs.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled);
 }
 
 bool IsExtendedReportingPolicyManaged(const PrefService& prefs) {
-  return prefs.IsManagedPreference(GetExtendedReportingPrefName(prefs));
+  return prefs.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled);
 }
 
 void RecordExtendedReportingMetrics(const PrefService& prefs) {
@@ -262,12 +257,12 @@
     PrefService* prefs,
     bool value,
     ExtendedReportingOptInLocation location) {
-  prefs->SetBoolean(GetExtendedReportingPrefName(*prefs), value);
+  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
   RecordExtendedReportingPrefChanged(*prefs, location);
 }
 
 void SetExtendedReportingPref(PrefService* prefs, bool value) {
-  prefs->SetBoolean(GetExtendedReportingPrefName(*prefs), value);
+  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
 }
 
 void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
diff --git a/components/safe_browsing/common/safe_browsing_prefs.h b/components/safe_browsing/common/safe_browsing_prefs.h
index a90d70b6..6d68d3f 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.h
+++ b/components/safe_browsing/common/safe_browsing_prefs.h
@@ -149,10 +149,6 @@
 // Returns the level of reporting available for the current user.
 ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs);
 
-// Returns the name of the Safe Browsing Extended Reporting pref that is
-// currently in effect. The specific pref in-use may change through experiments.
-const char* GetExtendedReportingPrefName(const PrefService& prefs);
-
 // Returns whether the user is able to modify the Safe Browsing Extended
 // Reporting opt-in.
 bool IsExtendedReportingOptInAllowed(const PrefService& prefs);
diff --git a/components/safe_browsing/common/safe_browsing_prefs_unittest.cc b/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
index cc3cb04..d4a2629 100644
--- a/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
+++ b/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
@@ -61,21 +61,6 @@
         base::JoinString(disabled_features, ","));
   }
 
-  std::string GetActivePref() { return GetExtendedReportingPrefName(prefs_); }
-
-  // Convenience method for explicitly setting up all combinations of prefs and
-  // experiments.
-  void TestGetPrefName(bool scout_reporting,
-                       bool scout_group,
-                       bool can_show_scout,
-                       const std::string& expected_pref) {
-    ResetPrefs(scout_reporting, scout_group);
-    ResetExperiments(can_show_scout);
-    EXPECT_EQ(expected_pref, GetActivePref())
-        << " scout=" << scout_reporting << " scout_group=" << scout_group
-        << " can_show_scout=" << can_show_scout;
-  }
-
   bool IsScoutGroupSelected() {
     return prefs_.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected);
   }
@@ -104,59 +89,6 @@
   content::TestBrowserThreadBundle thread_bundle_;
 };
 
-// This test ensures that we correctly select Scout as the
-// active preference in a number of common scenarios.
-// TODO(crbug.com/881476) disabled for flaky crashes.
-#if defined(OS_WIN)
-#define MAYBE_GetExtendedReportingPrefName_Common \
-  DISABLED_GetExtendedReportingPrefName_Common
-#else
-#define MAYBE_GetExtendedReportingPrefName_Common \
-  GetExtendedReportingPrefName_Common
-#endif
-TEST_F(SafeBrowsingPrefsTest, MAYBE_GetExtendedReportingPrefName_Common) {
-  const std::string& scout = prefs::kSafeBrowsingScoutReportingEnabled;
-
-  // By default (all prefs and experiment features disabled), Scout pref is
-  // used.
-  TestGetPrefName(false, false, false, scout);
-
-  // Changing any prefs (including ScoutGroupSelected) keeps Scout as the active
-  // pref because the experiment remains in the Control group.
-  TestGetPrefName(/*scout=*/true, false, false, scout);
-  TestGetPrefName(false, /*scout_group=*/true, false, scout);
-
-  // Being in the experiment group with ScoutGroup selected makes Scout the
-  // active pref.
-  TestGetPrefName(false, /*scout_group=*/true, /*can_show_scout=*/true, scout);
-
-  // When ScoutGroup is not selected then Scout still remains the active pref,
-  // regardless if the experiment is enabled.
-  TestGetPrefName(false, false, /*can_show_scout=*/true, scout);
-}
-
-// Here we exhaustively check all combinations of pref and experiment states.
-// This should help catch regressions.
-// TODO(crbug.com/881476) disabled for flaky crashes.
-#if defined(OS_WIN)
-#define MAYBE_GetExtendedReportingPrefName_Exhaustive \
-  DISABLED_GetExtendedReportingPrefName_Exhaustive
-#else
-#define MAYBE_GetExtendedReportingPrefName_Exhaustive \
-  GetExtendedReportingPrefName_Exhaustive
-#endif
-TEST_F(SafeBrowsingPrefsTest, MAYBE_GetExtendedReportingPrefName_Exhaustive) {
-  const std::string& scout = prefs::kSafeBrowsingScoutReportingEnabled;
-  TestGetPrefName(false, false, false, scout);
-  TestGetPrefName(false, false, true, scout);
-  TestGetPrefName(false, true, false, scout);
-  TestGetPrefName(false, true, true, scout);
-  TestGetPrefName(true, false, false, scout);
-  TestGetPrefName(true, false, true, scout);
-  TestGetPrefName(true, true, false, scout);
-  TestGetPrefName(true, true, true, scout);
-}
-
 // TODO(crbug.com/881476) disabled for flaky crashes.
 #if defined(OS_WIN)
 #define MAYBE_GetSafeBrowsingExtendedReportingLevel \
@@ -257,9 +189,10 @@
   // Make the SBER pref managed and enable it and ensure that the pref gets
   // the expected value. Making SBER managed doesn't change the
   // SBEROptInAllowed setting.
-  prefs_.SetManagedPref(GetExtendedReportingPrefName(prefs_),
+  prefs_.SetManagedPref(prefs::kSafeBrowsingScoutReportingEnabled,
                         std::make_unique<base::Value>(true));
-  EXPECT_TRUE(prefs_.IsManagedPreference(GetExtendedReportingPrefName(prefs_)));
+  EXPECT_TRUE(
+      prefs_.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled));
   // The value of the pref comes from the policy.
   EXPECT_TRUE(IsExtendedReportingEnabled(prefs_));
   // SBER being managed doesn't change the SBEROptInAllowed pref.
diff --git a/components/security_interstitials/content/security_interstitial_controller_client.cc b/components/security_interstitials/content/security_interstitial_controller_client.cc
index 49eba9c..3a88df42 100644
--- a/components/security_interstitials/content/security_interstitial_controller_client.cc
+++ b/components/security_interstitials/content/security_interstitial_controller_client.cc
@@ -99,7 +99,7 @@
 
 const std::string
 SecurityInterstitialControllerClient::GetExtendedReportingPrefName() const {
-  return safe_browsing::GetExtendedReportingPrefName(*prefs_);
+  return prefs::kSafeBrowsingScoutReportingEnabled;
 }
 
 bool SecurityInterstitialControllerClient::CanLaunchDateAndTimeSettings() {
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index dad2ab6..988a954a 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -10,6 +10,7 @@
 
 #include "base/callback_forward.h"
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
diff --git a/components/signin/core/browser/profile_management_switches.cc b/components/signin/core/browser/profile_management_switches.cc
index 9321f3a8..a0246df 100644
--- a/components/signin/core/browser/profile_management_switches.cc
+++ b/components/signin/core/browser/profile_management_switches.cc
@@ -4,23 +4,15 @@
 
 #include "components/signin/core/browser/profile_management_switches.h"
 
-#include <string>
-
-#include "base/command_line.h"
 #include "base/logging.h"
-#include "base/metrics/field_trial_params.h"
-#include "build/build_config.h"
-#include "components/signin/core/browser/signin_switches.h"
 
 namespace signin {
 
 namespace {
-
 bool AccountConsistencyMethodGreaterOrEqual(AccountConsistencyMethod a,
                                             AccountConsistencyMethod b) {
   return static_cast<int>(a) >= static_cast<int>(b);
 }
-
 }  // namespace
 
 bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a,
@@ -30,15 +22,4 @@
   return AccountConsistencyMethodGreaterOrEqual(a, b);
 }
 
-bool IsExtensionsMultiAccount() {
-#if defined(OS_ANDROID) || defined(OS_IOS)
-  NOTREACHED() << "Extensions are not enabled on Android or iOS";
-  // Account consistency is enabled on Android and iOS.
-  return false;
-#endif
-
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kExtensionsMultiAccount);
-}
-
 }  // namespace signin
diff --git a/components/signin/core/browser/profile_management_switches.h b/components/signin/core/browser/profile_management_switches.h
index 51bc3e6..702972cf 100644
--- a/components/signin/core/browser/profile_management_switches.h
+++ b/components/signin/core/browser/profile_management_switches.h
@@ -9,8 +9,6 @@
 #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
 #define COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
 
-#include "base/feature_list.h"
-
 namespace signin {
 
 // TODO(https://crbug.com/777774): Cleanup this enum and remove related
@@ -41,12 +39,6 @@
 bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a,
                               AccountConsistencyMethod b);
 
-////////////////////////////////////////////////////////////////////////////////
-// Other functions:
-
-// Whether the chrome.identity API should be multi-account.
-bool IsExtensionsMultiAccount();
-
 }  // namespace signin
 
 #endif  // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
diff --git a/components/signin/core/browser/signin_switches.cc b/components/signin/core/browser/signin_switches.cc
index 1886ccf3..7d99de9b 100644
--- a/components/signin/core/browser/signin_switches.cc
+++ b/components/signin/core/browser/signin_switches.cc
@@ -26,7 +26,4 @@
 const char kAccountConsistencyDice[] = "dice";
 #endif
 
-// Enables multiple account versions of chrome.identity APIs.
-const char kExtensionsMultiAccount[] = "extensions-multi-account";
-
 }  // namespace switches
diff --git a/components/signin/core/browser/signin_switches.h b/components/signin/core/browser/signin_switches.h
index 71cf2a5..7d7971f 100644
--- a/components/signin/core/browser/signin_switches.h
+++ b/components/signin/core/browser/signin_switches.h
@@ -17,7 +17,6 @@
 // alongside the definition of their values in the .cc file.
 extern const char kClearTokenService[];
 extern const char kDisableSigninScopedDeviceId[];
-extern const char kExtensionsMultiAccount[];
 
 #if !BUILDFLAG(ENABLE_MIRROR)
 // Note: Account consistency (Mirror) is already enabled on mobile platforms, so
diff --git a/components/sync/model/sync_data.cc b/components/sync/model/sync_data.cc
index eb57484..04a3b385 100644
--- a/components/sync/model/sync_data.cc
+++ b/components/sync/model/sync_data.cc
@@ -169,8 +169,7 @@
   // cases, where this is the hashed tag value. The original tag is not sent to
   // the server so we wouldn't be able to set this value anyways. The only way
   // to recreate an un-hashed tag is for the service to do so with a specifics.
-  // Should only be used by sessions, see crbug.com/604657.
-  DCHECK_EQ(SESSIONS, GetDataType());
+  DCHECK(!immutable_entity_.Get().client_defined_unique_tag().empty());
   return immutable_entity_.Get().client_defined_unique_tag();
 }
 
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
index e604393..5f11020 100644
--- a/components/sync/model_impl/syncable_service_based_bridge.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -108,20 +108,24 @@
         store_->CreateWriteBatch();
 
     for (const SyncChange& change : change_list) {
-      DCHECK(change.sync_data().IsLocal())
-          << " from " << change.location().ToString();
-      SyncDataLocal sync_data(change.sync_data());
-      DCHECK(sync_data.IsValid()) << " from " << change.location().ToString();
-      const std::string storage_key =
-          GenerateSyncableHash(type_, sync_data.GetTag());
-      DCHECK(!storage_key.empty());
-
       switch (change.change_type()) {
         case SyncChange::ACTION_INVALID:
           NOTREACHED() << " from " << change.location().ToString();
           break;
+
         case SyncChange::ACTION_ADD:
         case SyncChange::ACTION_UPDATE: {
+          DCHECK_EQ(type_, change.sync_data().GetDataType());
+          DCHECK(change.sync_data().IsLocal())
+              << " from " << change.location().ToString();
+
+          SyncDataLocal sync_data(change.sync_data());
+          DCHECK(sync_data.IsValid())
+              << " from " << change.location().ToString();
+          const std::string storage_key =
+              GenerateSyncableHash(type_, sync_data.GetTag());
+          DCHECK(!storage_key.empty());
+
           (*in_memory_store_)[storage_key] = sync_data.GetSpecifics();
           std::unique_ptr<sync_pb::PersistedEntityData> persisted_entity =
               CreatePersistedFromSyncData(sync_data);
@@ -133,11 +137,35 @@
               batch->GetMetadataChangeList());
           break;
         }
-        case SyncChange::ACTION_DELETE:
+
+        case SyncChange::ACTION_DELETE: {
+          std::string storage_key;
+          // Both SyncDataLocal and SyncDataRemote are allowed for deletions.
+          if (change.sync_data().IsLocal()) {
+            SyncDataLocal sync_data(change.sync_data());
+            DCHECK(sync_data.IsValid())
+                << " from " << change.location().ToString();
+            storage_key = GenerateSyncableHash(type_, sync_data.GetTag());
+          } else {
+            SyncDataRemote sync_data(change.sync_data());
+            storage_key = sync_data.GetClientTagHash();
+          }
+
+          DCHECK(!storage_key.empty())
+              << " from " << change.location().ToString();
+
           in_memory_store_->erase(storage_key);
-          other_->Delete(storage_key, batch->GetMetadataChangeList());
           batch->DeleteData(storage_key);
+
+          if (IsActOnceDataType(type_)) {
+            batch->GetMetadataChangeList()->ClearMetadata(storage_key);
+            other_->UntrackEntityForStorageKey(storage_key);
+          } else {
+            other_->Delete(storage_key, batch->GetMetadataChangeList());
+          }
+
           break;
+        }
       }
     }
 
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc
index a65fdd38..0ac9f80 100644
--- a/components/update_client/ping_manager_unittest.cc
+++ b/components/update_client/ping_manager_unittest.cc
@@ -135,10 +135,10 @@
         R"(prodchannel="fake_channel_string" )"
         R"(os="\w+" arch="\w+" nacl_arch="[-\w]+"( wow64="1")?>)"
         R"(<hw physmemory="\d+"/>)"
-        R"(<os platform="Fake Operating System" arch="\w+" )"
+        R"(<os platform="Fake Operating System" arch="[,-.\w]+" )"
         R"(version="[-.\w]+"( sp="[\s\w]+")?/>)"
         R"(<app appid="abc"><event eventtype="3" eventresult="1" )"
-        R"(previousversion="1.0" nextversion="2.0"/></app></request>)";
+        R"(previousversion="1\.0" nextversion="2\.0"/></app></request>)";
     EXPECT_TRUE(RE2::FullMatch(msg, regex)) << msg;
 
     // Check the ping request does not carry the specific extra request headers.
@@ -170,7 +170,7 @@
     const auto msg = interceptor->GetRequestBody(0);
     constexpr char regex[] =
         R"(<app appid="abc"><event eventtype="3" eventresult="0" )"
-        R"(previousversion="1.0" nextversion="2.0"/></app>)";
+        R"(previousversion="1\.0" nextversion="2\.0"/></app>)";
     EXPECT_TRUE(RE2::PartialMatch(msg, regex)) << msg;
     interceptor->Reset();
   }
@@ -206,7 +206,7 @@
         R"(errorcode="2" extracode1="-1" diffresult="0" )"
         R"(differrorcat="4" differrorcode="20" diffextracode1="-10" )"
         R"(previousfp="prev fp" nextfp="next fp" )"
-        R"(previousversion="1.0" nextversion="2.0"/></app>)";
+        R"(previousversion="1\.0" nextversion="2\.0"/></app>)";
     EXPECT_TRUE(RE2::PartialMatch(msg, regex)) << msg;
 
     interceptor->Reset();
@@ -230,7 +230,7 @@
     const auto msg = interceptor->GetRequestBody(0);
     constexpr char regex[] =
         R"(<app appid="abc"><event eventtype="3" eventresult="0" )"
-        R"(previousversion="1.0"/></app>)";
+        R"(previousversion="1\.0"/></app>)";
     EXPECT_TRUE(RE2::PartialMatch(msg, regex)) << msg;
 
     interceptor->Reset();
@@ -252,7 +252,7 @@
     const auto msg = interceptor->GetRequestBody(0);
     constexpr char regex[] =
         R"(<app appid="abc"><event eventtype="4" eventresult="1" )"
-        R"(previousversion="1.2.3.4" nextversion="0"/></app>)";
+        R"(previousversion="1\.2\.3\.4" nextversion="0"/></app>)";
     EXPECT_TRUE(RE2::PartialMatch(msg, regex)) << msg;
 
     interceptor->Reset();
@@ -296,15 +296,15 @@
     constexpr char regex[] =
         R"(<app appid="abc">)"
         R"(<event eventtype="3" eventresult="1" )"
-        R"(previousversion="1.0" nextversion="2.0"/>)"
+        R"(previousversion="1\.0" nextversion="2\.0"/>)"
         R"(<event eventtype="14" eventresult="0" downloader="direct" )"
         R"(errorcode="-1" url="http://host1/path1" downloaded="123" )"
-        R"(total="456" download_time_ms="987" previousversion="1.0" )"
-        R"(nextversion="2.0"/>)"
+        R"(total="456" download_time_ms="987" previousversion="1\.0" )"
+        R"(nextversion="2\.0"/>)"
         R"(<event eventtype="14" eventresult="1" downloader="bits" )"
         R"(url="http://host2/path2" downloaded="1230" total="4560" )"
-        R"(download_time_ms="9870" previousversion="1.0" )"
-        R"(nextversion="2.0"/></app>)";
+        R"(download_time_ms="9870" previousversion="1\.0" )"
+        R"(nextversion="2\.0"/></app>)";
     EXPECT_TRUE(RE2::PartialMatch(msg, regex)) << msg;
 
     interceptor->Reset();
diff --git a/components/variations/synthetic_trial_registry.h b/components/variations/synthetic_trial_registry.h
index 646c90d..4be74aff 100644
--- a/components/variations/synthetic_trial_registry.h
+++ b/components/variations/synthetic_trial_registry.h
@@ -50,7 +50,7 @@
   // is registered for a given trial name will be recorded. The values passed
   // in must not correspond to any real field trial in the code.
   // Note: Should not be used to replace trials that were registered with
-  // RegisterMultiGroupSyntheticFieldTrial().
+  // RegisterSyntheticMultiGroupFieldTrial().
   void RegisterSyntheticFieldTrial(const SyntheticTrialGroup& trial_group);
 
   // Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial
diff --git a/content/browser/android/java/gin_java_bridge_message_filter.cc b/content/browser/android/java/gin_java_bridge_message_filter.cc
index 9c2feb1a..2a38722 100644
--- a/content/browser/android/java/gin_java_bridge_message_filter.cc
+++ b/content/browser/android/java/gin_java_bridge_message_filter.cc
@@ -84,6 +84,21 @@
   }
 }
 
+void GinJavaBridgeMessageFilter::RenderProcessExited(
+    RenderProcessHost* rph,
+    const ChildProcessTerminationInfo& info) {
+#if DCHECK_IS_ON()
+  {
+    scoped_refptr<GinJavaBridgeMessageFilter> filter =
+        base::UserDataAdapter<GinJavaBridgeMessageFilter>::Get(
+            rph, kGinJavaBridgeMessageFilterKey);
+    DCHECK_EQ(this, filter.get());
+  }
+#endif
+  rph->RemoveObserver(this);
+  rph->RemoveUserData(kGinJavaBridgeMessageFilterKey);
+}
+
 // static
 scoped_refptr<GinJavaBridgeMessageFilter> GinJavaBridgeMessageFilter::FromHost(
     GinJavaBridgeDispatcherHost* host, bool create_if_not_exists) {
@@ -94,6 +109,8 @@
   if (!filter && create_if_not_exists) {
     filter = new GinJavaBridgeMessageFilter();
     rph->AddFilter(filter.get());
+    rph->AddObserver(filter.get());
+
     rph->SetUserData(
         kGinJavaBridgeMessageFilterKey,
         std::make_unique<base::UserDataAdapter<GinJavaBridgeMessageFilter>>(
diff --git a/content/browser/android/java/gin_java_bridge_message_filter.h b/content/browser/android/java/gin_java_bridge_message_filter.h
index f38f0d032..c15bc707 100644
--- a/content/browser/android/java/gin_java_bridge_message_filter.h
+++ b/content/browser/android/java/gin_java_bridge_message_filter.h
@@ -16,6 +16,7 @@
 #include "content/browser/android/java/gin_java_bound_object.h"
 #include "content/common/android/gin_java_bridge_errors.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "content/public/browser/render_process_host_observer.h"
 
 namespace base {
 class ListValue;
@@ -30,7 +31,8 @@
 class GinJavaBridgeDispatcherHost;
 class RenderFrameHost;
 
-class GinJavaBridgeMessageFilter : public BrowserMessageFilter {
+class GinJavaBridgeMessageFilter : public BrowserMessageFilter,
+                                   public RenderProcessHostObserver {
  public:
   // BrowserMessageFilter
   void OnDestruct() const override;
@@ -38,6 +40,10 @@
   base::TaskRunner* OverrideTaskRunnerForMessage(
       const IPC::Message& message) override;
 
+  // RenderProcessHostObserver
+  void RenderProcessExited(RenderProcessHost* rph,
+                           const ChildProcessTerminationInfo& info) override;
+
   // Called on the UI thread.
   void AddRoutingIdForHost(GinJavaBridgeDispatcherHost* host,
                            RenderFrameHost* render_frame_host);
diff --git a/content/browser/background_fetch/background_fetch_context.cc b/content/browser/background_fetch/background_fetch_context.cc
index e95ff15..655baf1 100644
--- a/content/browser/background_fetch/background_fetch_context.cc
+++ b/content/browser/background_fetch/background_fetch_context.cc
@@ -452,6 +452,7 @@
     case FailureReason::CANCELLED_FROM_UI:
       CleanupRegistration(registration_id, {},
                           blink::mojom::BackgroundFetchResult::FAILURE);
+      registration_notifier_->Notify(*registration);
       event_dispatcher_.DispatchBackgroundFetchAbortEvent(
           registration_id, std::move(registration), base::DoNothing());
       return;
@@ -509,6 +510,7 @@
   switch (registration->failure_reason) {
     case FailureReason::NONE:
       registration->result = blink::mojom::BackgroundFetchResult::SUCCESS;
+      registration_notifier_->Notify(*registration);
       event_dispatcher_.DispatchBackgroundFetchSuccessEvent(
           registration_id, std::move(registration),
           base::BindOnce(
@@ -529,6 +531,7 @@
     case FailureReason::QUOTA_EXCEEDED:
     case FailureReason::TOTAL_DOWNLOAD_SIZE_EXCEEDED:
       registration->result = blink::mojom::BackgroundFetchResult::FAILURE;
+      registration_notifier_->Notify(*registration);
       event_dispatcher_.DispatchBackgroundFetchFailEvent(
           registration_id, std::move(registration),
           base::BindOnce(
diff --git a/content/browser/background_fetch/background_fetch_job_controller.cc b/content/browser/background_fetch/background_fetch_job_controller.cc
index aae90ba..7561aeb 100644
--- a/content/browser/background_fetch/background_fetch_job_controller.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller.cc
@@ -160,9 +160,10 @@
 
   active_request_downloaded_bytes_ = bytes_downloaded;
 
-  progress_callback_.Run(registration_id().unique_id(), options_.download_total,
-                         complete_requests_downloaded_bytes_cache_ +
-                             GetInProgressDownloadedBytes());
+  auto registration =
+      NewRegistration(blink::mojom::BackgroundFetchResult::UNSET);
+  registration->downloaded += GetInProgressDownloadedBytes();
+  progress_callback_.Run(*registration);
 }
 
 void BackgroundFetchJobController::DidCompleteRequest(
diff --git a/content/browser/background_fetch/background_fetch_job_controller.h b/content/browser/background_fetch/background_fetch_job_controller.h
index 56d0941..b63f389d 100644
--- a/content/browser/background_fetch/background_fetch_job_controller.h
+++ b/content/browser/background_fetch/background_fetch_job_controller.h
@@ -43,9 +43,7 @@
       base::OnceCallback<void(const BackgroundFetchRegistrationId&,
                               blink::mojom::BackgroundFetchFailureReason)>;
   using ProgressCallback =
-      base::RepeatingCallback<void(const std::string& /* unique_id */,
-                                   uint64_t /* download_total */,
-                                   uint64_t /* downloaded */)>;
+      base::RepeatingCallback<void(const BackgroundFetchRegistration&)>;
 
   BackgroundFetchJobController(
       BackgroundFetchDelegateProxy* delegate_proxy,
diff --git a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
index 265be30..259b466 100644
--- a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
@@ -207,10 +207,8 @@
   BackgroundFetchDelegate* delegate_;
 
  private:
-  void DidUpdateProgress(const std::string& unique_id,
-                         uint64_t download_total,
-                         uint64_t downloaded) {
-    last_downloaded_ = downloaded;
+  void DidUpdateProgress(const BackgroundFetchRegistration& registration) {
+    last_downloaded_ = registration.downloaded;
 
     if (job_progress_closure_)
       job_progress_closure_.Run();
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier.cc b/content/browser/background_fetch/background_fetch_registration_notifier.cc
index 75bba45..e5dfc98 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier.cc
+++ b/content/browser/background_fetch/background_fetch_registration_notifier.cc
@@ -28,14 +28,14 @@
   observers_.emplace(unique_id, std::move(observer));
 }
 
-void BackgroundFetchRegistrationNotifier::Notify(const std::string& unique_id,
-                                                 uint64_t download_total,
-                                                 uint64_t downloaded) {
-  auto range = observers_.equal_range(unique_id);
+void BackgroundFetchRegistrationNotifier::Notify(
+    const BackgroundFetchRegistration& registration) {
+  auto range = observers_.equal_range(registration.unique_id);
   for (auto it = range.first; it != range.second; ++it) {
     // TODO(crbug.com/774054): Uploads are not yet supported.
     it->second->OnProgress(0 /* upload_total */, 0 /* uploaded */,
-                           download_total, downloaded);
+                           registration.download_total, registration.downloaded,
+                           registration.result, registration.failure_reason);
   }
 }
 
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier.h b/content/browser/background_fetch/background_fetch_registration_notifier.h
index e14df6f5..06324735 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier.h
+++ b/content/browser/background_fetch/background_fetch_registration_notifier.h
@@ -30,12 +30,10 @@
       const std::string& unique_id,
       blink::mojom::BackgroundFetchRegistrationObserverPtr observer);
 
-  // Notifies any registered observers for the registration identified by the
-  // |unique_id| of the progress. This will cause JavaScript events to fire.
-  // Successful fetches must also call Notify with the final state.
-  void Notify(const std::string& unique_id,
-              uint64_t download_total,
-              uint64_t downloaded);
+  // Notifies any registered observers for the |registration| of the progress.
+  // This will cause JavaScript events to fire.
+  // Completed fetches must also call Notify with the final state.
+  void Notify(const BackgroundFetchRegistration& registration);
 
   // Notifies any registered observers for the registration identifier by
   // |unique_id| that the records for the fetch are no longer available.
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc b/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
index b97eed5..cd9eee1 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
@@ -19,6 +19,7 @@
 namespace content {
 namespace {
 
+const char kDeveloperId[] = "my-fetch";
 const char kPrimaryUniqueId[] = "7e57ab1e-c0de-a150-ca75-1e75f005ba11";
 const char kSecondaryUniqueId[] = "bb48a9fb-c21f-4c2d-a9ae-58bd48a9fb53";
 
@@ -32,16 +33,24 @@
     ProgressUpdate(uint64_t upload_total,
                    uint64_t uploaded,
                    uint64_t download_total,
-                   uint64_t downloaded)
+                   uint64_t downloaded,
+                   blink::mojom::BackgroundFetchResult result,
+                   blink::mojom::BackgroundFetchFailureReason failure_reason)
         : upload_total(upload_total),
           uploaded(uploaded),
           download_total(download_total),
-          downloaded(downloaded) {}
+          downloaded(downloaded),
+          result(result),
+          failure_reason(failure_reason) {}
 
     uint64_t upload_total = 0;
     uint64_t uploaded = 0;
     uint64_t download_total = 0;
     uint64_t downloaded = 0;
+    blink::mojom::BackgroundFetchResult result =
+        blink::mojom::BackgroundFetchResult::UNSET;
+    blink::mojom::BackgroundFetchFailureReason failure_reason =
+        blink::mojom::BackgroundFetchFailureReason::NONE;
   };
 
   TestRegistrationObserver() : binding_(this) {}
@@ -65,12 +74,15 @@
   bool records_available() const { return records_available_; }
 
   // blink::mojom::BackgroundFetchRegistrationObserver implementation.
-  void OnProgress(uint64_t upload_total,
-                  uint64_t uploaded,
-                  uint64_t download_total,
-                  uint64_t downloaded) override {
+  void OnProgress(
+      uint64_t upload_total,
+      uint64_t uploaded,
+      uint64_t download_total,
+      uint64_t downloaded,
+      blink::mojom::BackgroundFetchResult result,
+      blink::mojom::BackgroundFetchFailureReason failure_reason) override {
     progress_updates_.emplace_back(upload_total, uploaded, download_total,
-                                   downloaded);
+                                   downloaded, result, failure_reason);
   }
 
   void OnRecordsUnavailable() override { records_available_ = false; }
@@ -94,10 +106,8 @@
 
   // Notifies all observers for the |unique_id| of the made progress, and waits
   // until the task runner managing the Mojo connection has finished.
-  void Notify(const std::string& unique_id,
-              uint64_t download_total,
-              uint64_t downloaded) {
-    notifier_->Notify(unique_id, download_total, downloaded);
+  void Notify(const BackgroundFetchRegistration& registration) {
+    notifier_->Notify(registration);
     task_runner_->RunUntilIdle();
   }
 
@@ -122,7 +132,11 @@
   notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
-  Notify(kPrimaryUniqueId, kDownloadTotal, kDownloaded);
+  Notify(BackgroundFetchRegistration(
+      kDeveloperId, kPrimaryUniqueId,
+      /* upload_total*/ 0, /* uploaded*/ 0, kDownloadTotal, kDownloaded,
+      blink::mojom::BackgroundFetchResult::UNSET,
+      blink::mojom::BackgroundFetchFailureReason::NONE));
 
   ASSERT_EQ(observer->progress_updates().size(), 1u);
 
@@ -132,6 +146,9 @@
   EXPECT_EQ(update.uploaded, 0u);
   EXPECT_EQ(update.download_total, kDownloadTotal);
   EXPECT_EQ(update.downloaded, kDownloaded);
+  EXPECT_EQ(update.result, blink::mojom::BackgroundFetchResult::UNSET);
+  EXPECT_EQ(update.failure_reason,
+            blink::mojom::BackgroundFetchFailureReason::NONE);
 }
 
 TEST_F(BackgroundFetchRegistrationNotifierTest, NotifyMultipleObservers) {
@@ -151,7 +168,11 @@
   ASSERT_EQ(secondary_observer->progress_updates().size(), 0u);
 
   // Notify the |kPrimaryUniqueId|.
-  Notify(kPrimaryUniqueId, kDownloadTotal, kDownloaded);
+  Notify(BackgroundFetchRegistration(
+      kDeveloperId, kPrimaryUniqueId,
+      /* upload_total*/ 0, /* uploaded*/ 0, kDownloadTotal, kDownloaded,
+      blink::mojom::BackgroundFetchResult::UNSET,
+      blink::mojom::BackgroundFetchFailureReason::NONE));
 
   for (auto& observer : primary_observers) {
     ASSERT_EQ(observer->progress_updates().size(), 1u);
@@ -162,6 +183,9 @@
     EXPECT_EQ(update.uploaded, 0u);
     EXPECT_EQ(update.download_total, kDownloadTotal);
     EXPECT_EQ(update.downloaded, kDownloaded);
+    EXPECT_EQ(update.result, blink::mojom::BackgroundFetchResult::UNSET);
+    EXPECT_EQ(update.failure_reason,
+              blink::mojom::BackgroundFetchFailureReason::NONE);
   }
 
   // The observer for |kSecondaryUniqueId| should not have been notified.
@@ -175,14 +199,22 @@
   notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
-  Notify(kPrimaryUniqueId, kDownloadTotal, kDownloaded);
+  Notify(BackgroundFetchRegistration(
+      kDeveloperId, kPrimaryUniqueId,
+      /* upload_total*/ 0, /* uploaded*/ 0, kDownloadTotal, kDownloaded,
+      blink::mojom::BackgroundFetchResult::UNSET,
+      blink::mojom::BackgroundFetchFailureReason::NONE));
 
   ASSERT_EQ(observer->progress_updates().size(), 1u);
 
   // Closes the binding as would be done from the renderer process.
   observer->Close();
 
-  Notify(kPrimaryUniqueId, kDownloadTotal, kDownloaded);
+  Notify(BackgroundFetchRegistration(
+      kDeveloperId, kPrimaryUniqueId,
+      /* upload_total*/ 0, /* uploaded*/ 0, kDownloadTotal, kDownloaded,
+      blink::mojom::BackgroundFetchResult::UNSET,
+      blink::mojom::BackgroundFetchFailureReason::NONE));
 
   // The observers for |kPrimaryUniqueId| were removed, so no second update
   // should have been received by the |observer|.
@@ -195,7 +227,11 @@
   notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
-  Notify(kSecondaryUniqueId, kDownloadTotal, kDownloaded);
+  Notify(BackgroundFetchRegistration(
+      kDeveloperId, kSecondaryUniqueId,
+      /* upload_total*/ 0, /* uploaded*/ 0, kDownloadTotal, kDownloaded,
+      blink::mojom::BackgroundFetchResult::UNSET,
+      blink::mojom::BackgroundFetchFailureReason::NONE));
 
   // Because the notification was for |kSecondaryUniqueId|, no progress updates
   // should be received by the |observer|.
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
index c1b02ef..60a5a64 100644
--- a/content/browser/child_process_launcher_helper_mac.cc
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -73,34 +73,8 @@
       command_line_->HasSwitch(service_manager::switches::kNoSandbox) ||
       service_manager::IsUnsandboxedSandboxType(sandbox_type);
 
-  // TODO(kerrnel): Delete this switch once the V2 sandbox is always enabled.
-  bool use_v2 = base::FeatureList::IsEnabled(features::kMacV2Sandbox);
-
-  switch (sandbox_type) {
-    case service_manager::SANDBOX_TYPE_NO_SANDBOX:
-      break;
-    case service_manager::SANDBOX_TYPE_CDM:
-    case service_manager::SANDBOX_TYPE_PPAPI:
-    case service_manager::SANDBOX_TYPE_RENDERER:
-    case service_manager::SANDBOX_TYPE_UTILITY:
-    case service_manager::SANDBOX_TYPE_NACL_LOADER:
-    case service_manager::SANDBOX_TYPE_PDF_COMPOSITOR:
-    case service_manager::SANDBOX_TYPE_PROFILING:
-      // If the feature experiment is enabled and this process type supports
-      // the v2 sandbox, use it.
-      use_v2 &= true;
-      break;
-    case service_manager::SANDBOX_TYPE_AUDIO:
-      // The audio service only exists with the v2 sandbox.
-      use_v2 |= true;
-      break;
-    default:
-      // This is a 'break' because the V2 sandbox is not enabled for all
-      // processes yet, and so there are sandbox types like NETWORK that
-      // should not be run under the V2 sandbox.
-      use_v2 = false;
-      break;
-  }
+  bool use_v2 =
+      !no_sandbox && (sandbox_type != service_manager::SANDBOX_TYPE_GPU);
 
   if (use_v2 && !no_sandbox) {
     // Generate the profile string.
@@ -133,8 +107,16 @@
       case service_manager::SANDBOX_TYPE_PROFILING:
         profile += service_manager::kSeatbeltPolicyString_utility;
         break;
-      default:
+      case service_manager::SANDBOX_TYPE_NETWORK:
+        // Put a separate CHECK() for the network sandbox so that crash reports
+        // will show which invalid case was hit.
         CHECK(false);
+        break;
+      case service_manager::SANDBOX_TYPE_INVALID:
+      case service_manager::SANDBOX_TYPE_FIRST_TYPE:
+      case service_manager::SANDBOX_TYPE_AFTER_LAST_TYPE:
+        CHECK(false);
+        break;
     }
 
     // Disable os logging to com.apple.diagnosticd which is a performance
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
index 269e705..8e370e5 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
@@ -87,6 +87,17 @@
                                           "Tinos",
                                           "Mukti Narrow",
                                           "Tinos"};
+#elif defined(OS_MACOSX)
+const char* kExpectedFontFamilyNames[] = {"American Typewriter",
+                                          "Arial Narrow",
+                                          "Baskerville",
+                                          "Devanagari MT",
+                                          "DIN Alternate",
+                                          "Gill Sans",
+                                          "Iowan Old Style",
+                                          "Malayalam Sangam MN",
+                                          "Hiragino Maru Gothic Pro",
+                                          "Hiragino Kaku Gothic StdN"};
 #endif
 
 }  // namespace
@@ -110,7 +121,7 @@
 };
 
 // TODO(drott): Enable this on all platforms.
-#if defined(OS_ANDROID) || defined(OS_LINUX)
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX)
 IN_PROC_BROWSER_TEST_F(FontUniqueNameBrowserTest, ContentLocalFontsMatching) {
   LoadAndWait("/font_src_local_matching.html");
   Attach();
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index df0aa51..a17ae7c 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -5207,10 +5207,9 @@
   BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
   DCHECK(browser_main_loop);
   if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) {
-    scoped_refptr<AudioInputDeviceManager> aidm =
-        browser_main_loop->media_stream_manager()->audio_input_device_manager();
-    audio_service_audio_input_stream_factory_.emplace(std::move(request),
-                                                      std::move(aidm), this);
+    MediaStreamManager* msm = browser_main_loop->media_stream_manager();
+    audio_service_audio_input_stream_factory_.emplace(std::move(request), msm,
+                                                      this);
   } else {
     in_content_audio_input_stream_factory_ =
         RenderFrameAudioInputStreamFactoryHandle::CreateFactory(
diff --git a/content/browser/media/audio_input_stream_broker.cc b/content/browser/media/audio_input_stream_broker.cc
index b5e91e560..0fc989d 100644
--- a/content/browser/media/audio_input_stream_broker.cc
+++ b/content/browser/media/audio_input_stream_broker.cc
@@ -6,23 +6,24 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/command_line.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/task/post_task.h"
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/media/media_internals.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/media_observer.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/common/content_client.h"
 #include "media/audio/audio_logging.h"
 #include "media/base/media_switches.h"
 #include "media/base/user_input_monitor.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 #if defined(OS_CHROMEOS)
@@ -82,7 +83,7 @@
       renderer_factory_client_(std::move(renderer_factory_client)),
       observer_binding_(this),
       weak_ptr_factory_(this) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(renderer_factory_client_);
   DCHECK(deleter_);
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("audio", "AudioInputStreamBroker", this);
@@ -91,11 +92,7 @@
   renderer_factory_client_.set_connection_error_handler(base::BindOnce(
       &AudioInputStreamBroker::ClientBindingLost, base::Unretained(this)));
 
-  // Notify RenderProcessHost about input stream so the renderer is not
-  // background.
-  auto* process_host = RenderProcessHost::FromID(render_process_id);
-  if (process_host)
-    process_host->OnMediaStreamAdded();
+  NotifyProcessHostOfStartedStream(render_process_id);
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kUseFakeDeviceForMediaStream)) {
@@ -111,7 +108,7 @@
 }
 
 AudioInputStreamBroker::~AudioInputStreamBroker() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
 #if defined(OS_CHROMEOS)
   if (params_.channel_layout() ==
@@ -125,9 +122,7 @@
   if (user_input_monitor_)
     user_input_monitor_->DisableKeyPressMonitoring();
 
-  auto* process_host = RenderProcessHost::FromID(render_process_id());
-  if (process_host)
-    process_host->OnMediaStreamRemoved();
+  NotifyProcessHostOfStoppedStream(render_process_id());
 
   // TODO(https://crbug.com/829317) update tab recording indicator.
 
@@ -145,7 +140,7 @@
 
 void AudioInputStreamBroker::CreateStream(
     audio::mojom::StreamFactory* factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!observer_binding_.is_bound());
   DCHECK(!client_request_);
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("audio", "CreateStream", this, "device id",
@@ -175,8 +170,6 @@
   // Note that the component id for AudioLog is used to differentiate between
   // several users of the same audio log. Since this audio log is for a single
   // stream, the component id used doesn't matter.
-  // TODO(https://crbug.com/836226) pass valid user input monitor handle when
-  // switching to audio service input streams.
   constexpr int log_component_id = 0;
   factory->CreateInputStream(
       std::move(stream_request), std::move(client), std::move(observer_ptr),
@@ -191,7 +184,7 @@
 }
 
 void AudioInputStreamBroker::DidStartRecording() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(https://crbug.com/829317) update tab recording indicator.
 }
 
@@ -200,7 +193,7 @@
     media::mojom::ReadOnlyAudioDataPipePtr data_pipe,
     bool initially_muted,
     const base::Optional<base::UnguessableToken>& stream_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   awaiting_created_ = false;
   TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "CreateStream", this, "success",
                                   !!data_pipe);
@@ -221,7 +214,7 @@
 void AudioInputStreamBroker::ObserverBindingLost(
     uint32_t reason,
     const std::string& description) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   const uint32_t maxValidReason = static_cast<uint32_t>(
       media::mojom::AudioInputStreamObserver::DisconnectReason::kMaxValue);
@@ -238,13 +231,14 @@
 }
 
 void AudioInputStreamBroker::ClientBindingLost() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   disconnect_reason_ = media::mojom::AudioInputStreamObserver::
       DisconnectReason::kTerminatedByClient;
   Cleanup();
 }
 
 void AudioInputStreamBroker::Cleanup() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   std::move(deleter_).Run(this);
 }
diff --git a/content/browser/media/audio_input_stream_broker.h b/content/browser/media/audio_input_stream_broker.h
index bc1ea80..397cb49 100644
--- a/content/browser/media/audio_input_stream_broker.h
+++ b/content/browser/media/audio_input_stream_broker.h
@@ -7,12 +7,13 @@
 
 #include <string>
 
+#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
 #include "content/browser/media/audio_stream_broker.h"
 #include "content/common/content_export.h"
 #include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
 #include "media/base/audio_parameters.h"
+#include "media/mojo/interfaces/audio_data_pipe.mojom.h"
 #include "media/mojo/interfaces/audio_input_stream.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/audio/public/mojom/audio_processing.mojom.h"
diff --git a/content/browser/media/audio_loopback_stream_broker.cc b/content/browser/media/audio_loopback_stream_broker.cc
index 5ce0ed678..1b83498 100644
--- a/content/browser/media/audio_loopback_stream_broker.cc
+++ b/content/browser/media/audio_loopback_stream_broker.cc
@@ -6,9 +6,14 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
 #include "base/unguessable_token.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_process_host.h"
+#include "services/audio/public/mojom/stream_factory.mojom.h"
 
 namespace content {
 
@@ -29,7 +34,7 @@
       renderer_factory_client_(std::move(renderer_factory_client)),
       observer_binding_(this),
       weak_ptr_factory_(this) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(source_);
   DCHECK(renderer_factory_client_);
   DCHECK(deleter_);
@@ -45,25 +50,21 @@
   // Notify the source that we are capturing from it.
   source_->AddLoopbackSink(this);
 
-  // Notify RenderProcessHost about the input stream, so that the destination
-  // renderer does not get background.
-  if (auto* process_host = RenderProcessHost::FromID(render_process_id))
-    process_host->OnMediaStreamAdded();
+  NotifyProcessHostOfStartedStream(render_process_id);
 }
 
 AudioLoopbackStreamBroker::~AudioLoopbackStreamBroker() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (source_)
     source_->RemoveLoopbackSink(this);
 
-  if (auto* process_host = RenderProcessHost::FromID(render_process_id()))
-    process_host->OnMediaStreamRemoved();
+  NotifyProcessHostOfStoppedStream(render_process_id());
 }
 
 void AudioLoopbackStreamBroker::CreateStream(
     audio::mojom::StreamFactory* factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!observer_binding_.is_bound());
   DCHECK(!client_request_);
   DCHECK(source_);
@@ -93,20 +94,20 @@
 }
 
 void AudioLoopbackStreamBroker::OnSourceGone() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // No further access to |source_| is allowed.
   source_ = nullptr;
   Cleanup();
 }
 
 void AudioLoopbackStreamBroker::DidStartRecording() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 void AudioLoopbackStreamBroker::StreamCreated(
     media::mojom::AudioInputStreamPtr stream,
     media::mojom::ReadOnlyAudioDataPipePtr data_pipe) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!data_pipe) {
     Cleanup();
@@ -121,7 +122,7 @@
 }
 
 void AudioLoopbackStreamBroker::Cleanup() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   std::move(deleter_).Run(this);
 }
 
diff --git a/content/browser/media/audio_loopback_stream_broker.h b/content/browser/media/audio_loopback_stream_broker.h
index e7425363..aa1f6f6 100644
--- a/content/browser/media/audio_loopback_stream_broker.h
+++ b/content/browser/media/audio_loopback_stream_broker.h
@@ -5,18 +5,25 @@
 #ifndef CONTENT_BROWSER_MEDIA_AUDIO_LOOPBACK_STREAM_BROKER_H_
 #define CONTENT_BROWSER_MEDIA_AUDIO_LOOPBACK_STREAM_BROKER_H_
 
+#include <cstdint>
 #include <string>
 
+#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "base/sequence_checker.h"
 #include "content/browser/media/audio_muting_session.h"
 #include "content/browser/media/audio_stream_broker.h"
 #include "content/common/content_export.h"
 #include "media/base/audio_parameters.h"
+#include "media/mojo/interfaces/audio_data_pipe.mojom.h"
 #include "media/mojo/interfaces/audio_input_stream.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/audio/public/mojom/stream_factory.mojom.h"
+
+namespace audio {
+namespace mojom {
+class StreamFactory;
+}
+}  // namespace audio
 
 namespace content {
 
@@ -46,7 +53,7 @@
   void DidStartRecording() final;
 
   // AudioStreamBroker::LoopbackSink
-  void OnSourceGone() override;
+  void OnSourceGone() final;
 
  private:
   void StreamCreated(media::mojom::AudioInputStreamPtr stream,
diff --git a/content/browser/media/audio_stream_broker.cc b/content/browser/media/audio_stream_broker.cc
index 5f58941e..875d810e 100644
--- a/content/browser/media/audio_stream_broker.cc
+++ b/content/browser/media/audio_stream_broker.cc
@@ -6,9 +6,15 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/task/post_task.h"
 #include "content/browser/media/audio_input_stream_broker.h"
 #include "content/browser/media/audio_loopback_stream_broker.h"
 #include "content/browser/media/audio_output_stream_broker.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
 
 namespace content {
 
@@ -82,6 +88,28 @@
       render_frame_id_(render_frame_id) {}
 AudioStreamBroker::~AudioStreamBroker() {}
 
+// static
+void AudioStreamBroker::NotifyProcessHostOfStartedStream(
+    int render_process_id) {
+  auto impl = [](int id) {
+    if (auto* process_host = RenderProcessHost::FromID(id))
+      process_host->OnMediaStreamAdded();
+  };
+  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                           base::BindOnce(impl, render_process_id));
+}
+
+// static
+void AudioStreamBroker::NotifyProcessHostOfStoppedStream(
+    int render_process_id) {
+  auto impl = [](int id) {
+    if (auto* process_host = RenderProcessHost::FromID(id))
+      process_host->OnMediaStreamRemoved();
+  };
+  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                           base::BindOnce(impl, render_process_id));
+}
+
 AudioStreamBrokerFactory::AudioStreamBrokerFactory() {}
 AudioStreamBrokerFactory::~AudioStreamBrokerFactory() {}
 
diff --git a/content/browser/media/audio_stream_broker.h b/content/browser/media/audio_stream_broker.h
index a5f0880..24d8928f 100644
--- a/content/browser/media/audio_stream_broker.h
+++ b/content/browser/media/audio_stream_broker.h
@@ -5,12 +5,13 @@
 #ifndef CONTENT_BROWSER_MEDIA_AUDIO_STREAM_BROKER_H_
 #define CONTENT_BROWSER_MEDIA_AUDIO_STREAM_BROKER_H_
 
+#include <cstdint>
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "content/common/content_export.h"
 #include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
 #include "media/mojo/interfaces/audio_input_stream.mojom.h"
@@ -36,7 +37,8 @@
 
 // An AudioStreamBroker is used to broker a connection between a client
 // (typically renderer) and the audio service. It also sets up all objects
-// used for monitoring the stream.
+// used for monitoring the stream. All AudioStreamBrokers are used on the IO
+// thread.
 class CONTENT_EXPORT AudioStreamBroker {
  public:
   class CONTENT_EXPORT LoopbackSink {
@@ -68,6 +70,13 @@
 
   virtual void CreateStream(audio::mojom::StreamFactory* factory) = 0;
 
+  // Thread-safe utility that notifies the process host identified by
+  // |render_process_id| of a started stream to ensure that the renderer is not
+  // backgrounded. Must be paired with a later call to
+  // NotifyRenderProcessOfStoppedStream()
+  static void NotifyProcessHostOfStartedStream(int render_process_id);
+  static void NotifyProcessHostOfStoppedStream(int render_process_id);
+
   int render_process_id() const { return render_process_id_; }
   int render_frame_id() const { return render_frame_id_; }
 
@@ -79,7 +88,8 @@
   DISALLOW_COPY_AND_ASSIGN(AudioStreamBroker);
 };
 
-// Used for dependency injection into ForwardingAudioStreamFactory.
+// Used for dependency injection into ForwardingAudioStreamFactory. Used on the
+// IO thread.
 class CONTENT_EXPORT AudioStreamBrokerFactory {
  public:
   static std::unique_ptr<AudioStreamBrokerFactory> CreateImpl();
diff --git a/content/browser/media/forwarding_audio_stream_factory.cc b/content/browser/media/forwarding_audio_stream_factory.cc
index a76d573..bcdea97 100644
--- a/content/browser/media/forwarding_audio_stream_factory.cc
+++ b/content/browser/media/forwarding_audio_stream_factory.cc
@@ -6,39 +6,174 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "media/base/audio_parameters.h"
 #include "media/base/user_input_monitor.h"
 #include "services/audio/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace content {
 
-ForwardingAudioStreamFactory::ForwardingAudioStreamFactory(
-    WebContents* web_contents,
+ForwardingAudioStreamFactory::Core::Core(
+    base::WeakPtr<ForwardingAudioStreamFactory> owner,
     media::UserInputMonitorBase* user_input_monitor,
     std::unique_ptr<service_manager::Connector> connector,
     std::unique_ptr<AudioStreamBrokerFactory> broker_factory)
-    : WebContentsObserver(web_contents),
-      user_input_monitor_(user_input_monitor),
-      connector_(std::move(connector)),
+    : user_input_monitor_(user_input_monitor),
+      owner_(std::move(owner)),
       broker_factory_(std::move(broker_factory)),
-      group_id_(base::UnguessableToken::Create()) {
+      group_id_(base::UnguessableToken::Create()),
+      connector_(std::move(connector)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(owner_);
   DCHECK(broker_factory_);
+  DCHECK(connector_);
 }
 
-ForwardingAudioStreamFactory::~ForwardingAudioStreamFactory() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ForwardingAudioStreamFactory::Core::~Core() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   for (AudioStreamBroker::LoopbackSink* sink : loopback_sinks_)
     sink->OnSourceGone();
 }
 
+void ForwardingAudioStreamFactory::Core::CreateInputStream(
+    int render_process_id,
+    int render_frame_id,
+    const std::string& device_id,
+    const media::AudioParameters& params,
+    uint32_t shared_memory_count,
+    bool enable_agc,
+    audio::mojom::AudioProcessingConfigPtr processing_config,
+    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // |this| owns |inputs_|, so Unretained is safe.
+  inputs_
+      .insert(broker_factory_->CreateAudioInputStreamBroker(
+          render_process_id, render_frame_id, device_id, params,
+          shared_memory_count, user_input_monitor_, enable_agc,
+          std::move(processing_config),
+          base::BindOnce(&ForwardingAudioStreamFactory::Core::RemoveInput,
+                         base::Unretained(this)),
+          std::move(renderer_factory_client)))
+      .first->get()
+      ->CreateStream(GetFactory());
+}
+
+void ForwardingAudioStreamFactory::Core::AssociateInputAndOutputForAec(
+    const base::UnguessableToken& input_stream_id,
+    const std::string& raw_output_device_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // Avoid spawning a factory if this for some reason gets called with an
+  // invalid |input_stream_id| before any streams are created.
+  if (!inputs_.empty()) {
+    GetFactory()->AssociateInputAndOutputForAec(input_stream_id,
+                                                raw_output_device_id);
+  }
+}
+
+void ForwardingAudioStreamFactory::Core::CreateOutputStream(
+    int render_process_id,
+    int render_frame_id,
+    const std::string& device_id,
+    const media::AudioParameters& params,
+    const base::Optional<base::UnguessableToken>& processing_id,
+    media::mojom::AudioOutputStreamProviderClientPtr client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // |this| owns |outputs_|, so Unretained is safe.
+  outputs_
+      .insert(broker_factory_->CreateAudioOutputStreamBroker(
+          render_process_id, render_frame_id, ++stream_id_counter_, device_id,
+          params, group_id_, processing_id,
+          base::BindOnce(&ForwardingAudioStreamFactory::Core::RemoveOutput,
+                         base::Unretained(this)),
+          std::move(client)))
+      .first->get()
+      ->CreateStream(GetFactory());
+}
+
+void ForwardingAudioStreamFactory::Core::CreateLoopbackStream(
+    int render_process_id,
+    int render_frame_id,
+    AudioStreamBroker::LoopbackSource* loopback_source,
+    const media::AudioParameters& params,
+    uint32_t shared_memory_count,
+    bool mute_source,
+    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(loopback_source);
+
+  TRACE_EVENT_BEGIN1("audio", "CreateLoopbackStream", "group",
+                     group_id_.GetLowForSerialization());
+
+  // |this| owns |inputs_|, so Unretained is safe.
+  inputs_
+      .insert(broker_factory_->CreateAudioLoopbackStreamBroker(
+          render_process_id, render_frame_id, loopback_source, params,
+          shared_memory_count, mute_source,
+          base::BindOnce(&ForwardingAudioStreamFactory::Core::RemoveInput,
+                         base::Unretained(this)),
+          std::move(renderer_factory_client)))
+      .first->get()
+      ->CreateStream(GetFactory());
+  TRACE_EVENT_END1("audio", "CreateLoopbackStream", "source",
+                   loopback_source->GetGroupID().GetLowForSerialization());
+}
+
+void ForwardingAudioStreamFactory::Core::SetMuted(bool muted) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_NE(muted, !!muter_);
+  TRACE_EVENT_INSTANT2("audio", "SetMuted", TRACE_EVENT_SCOPE_THREAD, "group",
+                       group_id_.GetLowForSerialization(), "muted", muted);
+
+  if (!muted) {
+    muter_.reset();
+    return;
+  }
+
+  muter_.emplace(group_id_);
+  if (remote_factory_)
+    muter_->Connect(remote_factory_.get());
+}
+
+void ForwardingAudioStreamFactory::Core::AddLoopbackSink(
+    AudioStreamBroker::LoopbackSink* sink) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  loopback_sinks_.insert(sink);
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&ForwardingAudioStreamFactory::LoopbackStreamStarted,
+                     owner_));
+}
+
+void ForwardingAudioStreamFactory::Core::RemoveLoopbackSink(
+    AudioStreamBroker::LoopbackSink* sink) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  loopback_sinks_.erase(sink);
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&ForwardingAudioStreamFactory::LoopbackStreamStopped,
+                     owner_));
+}
+
+const base::UnguessableToken& ForwardingAudioStreamFactory::Core::GetGroupID() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  return group_id();
+}
+
 // static
 ForwardingAudioStreamFactory* ForwardingAudioStreamFactory::ForFrame(
     RenderFrameHost* frame) {
@@ -52,154 +187,95 @@
   return contents->GetAudioStreamFactory();
 }
 
-void ForwardingAudioStreamFactory::CreateInputStream(
-    RenderFrameHost* frame,
-    const std::string& device_id,
-    const media::AudioParameters& params,
-    uint32_t shared_memory_count,
-    bool enable_agc,
-    audio::mojom::AudioProcessingConfigPtr processing_config,
-    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client) {
+// static
+ForwardingAudioStreamFactory::Core* ForwardingAudioStreamFactory::CoreForFrame(
+    RenderFrameHost* frame) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  const int process_id = frame ? frame->GetProcess()->GetID() : -1;
-  const int frame_id = frame ? frame->GetRoutingID() : -1;
-  inputs_
-      .insert(broker_factory_->CreateAudioInputStreamBroker(
-          process_id, frame_id, device_id, params, shared_memory_count,
-          user_input_monitor_, enable_agc, std::move(processing_config),
-          base::BindOnce(&ForwardingAudioStreamFactory::RemoveInput,
-                         base::Unretained(this)),
-          std::move(renderer_factory_client)))
-      .first->get()
-      ->CreateStream(GetFactory());
+  ForwardingAudioStreamFactory* forwarding_factory =
+      ForwardingAudioStreamFactory::ForFrame(frame);
+  return forwarding_factory ? forwarding_factory->core() : nullptr;
 }
 
-void ForwardingAudioStreamFactory::AssociateInputAndOutputForAec(
-    const base::UnguessableToken& input_stream_id,
-    const std::string& raw_output_device_id) {
+ForwardingAudioStreamFactory::ForwardingAudioStreamFactory(
+    WebContents* web_contents,
+    media::UserInputMonitorBase* user_input_monitor,
+    std::unique_ptr<service_manager::Connector> connector,
+    std::unique_ptr<AudioStreamBrokerFactory> broker_factory)
+    : WebContentsObserver(web_contents), core_(), weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // Avoid spawning a factory if this for some reason gets called with an
-  // invalid |input_stream_id| before any streams are created.
-  if (!inputs_.empty()) {
-    GetFactory()->AssociateInputAndOutputForAec(input_stream_id,
-                                                raw_output_device_id);
-  }
+  core_ =
+      std::make_unique<Core>(weak_ptr_factory_.GetWeakPtr(), user_input_monitor,
+                             std::move(connector), std::move(broker_factory));
 }
 
-void ForwardingAudioStreamFactory::CreateOutputStream(
-    RenderFrameHost* frame,
-    const std::string& device_id,
-    const media::AudioParameters& params,
-    const base::Optional<base::UnguessableToken>& processing_id,
-    media::mojom::AudioOutputStreamProviderClientPtr client) {
+ForwardingAudioStreamFactory::~ForwardingAudioStreamFactory() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  const int process_id = frame->GetProcess()->GetID();
-  const int frame_id = frame->GetRoutingID();
-
-  outputs_
-      .insert(broker_factory_->CreateAudioOutputStreamBroker(
-          process_id, frame_id, ++stream_id_counter_, device_id, params,
-          group_id_, processing_id,
-          base::BindOnce(&ForwardingAudioStreamFactory::RemoveOutput,
-                         base::Unretained(this)),
-          std::move(client)))
-      .first->get()
-      ->CreateStream(GetFactory());
+  // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
+  // as it doesn't post in case it is already executed on the right thread. That
+  // causes issues in unit tests where the UI thread and the IO thread are the
+  // same.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
 }
 
-void ForwardingAudioStreamFactory::CreateLoopbackStream(
-    RenderFrameHost* frame,
-    ForwardingAudioStreamFactory* loopback_source,
-    const media::AudioParameters& params,
-    uint32_t shared_memory_count,
-    bool mute_source,
-    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client) {
+void ForwardingAudioStreamFactory::LoopbackStreamStarted() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(loopback_source);
+  web_contents()->IncrementCapturerCount(gfx::Size());
+}
 
-  TRACE_EVENT_BEGIN1("audio", "CreateLoopbackStream", "group",
-                     group_id_.GetLowForSerialization());
-
-  const int process_id = frame ? frame->GetProcess()->GetID() : -1;
-  const int frame_id = frame ? frame->GetRoutingID() : -1;
-  inputs_
-      .insert(broker_factory_->CreateAudioLoopbackStreamBroker(
-          process_id, frame_id, loopback_source, params, shared_memory_count,
-          mute_source,
-          base::BindOnce(&ForwardingAudioStreamFactory::RemoveInput,
-                         base::Unretained(this)),
-          std::move(renderer_factory_client)))
-      .first->get()
-      ->CreateStream(GetFactory());
-  TRACE_EVENT_END1("audio", "CreateLoopbackStream", "source",
-                   loopback_source->group_id().GetLowForSerialization());
+void ForwardingAudioStreamFactory::LoopbackStreamStopped() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  web_contents()->DecrementCapturerCount();
 }
 
 void ForwardingAudioStreamFactory::SetMuted(bool muted) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_NE(muted, IsMuted());
-  TRACE_EVENT_INSTANT2("audio", "SetMuted", TRACE_EVENT_SCOPE_THREAD, "group",
-                       group_id_.GetLowForSerialization(), "muted", muted);
+  if (is_muted_ != muted) {
+    is_muted_ = muted;
 
-  if (!muted) {
-    muter_.reset();
-    return;
+    // Unretained is safe since the destruction of |core_| will be posted to the
+    // IO thread later.
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&Core::SetMuted, base::Unretained(core_.get()), muted));
   }
-
-  muter_.emplace(group_id_);
-  if (remote_factory_)
-    muter_->Connect(remote_factory_.get());
 }
 
 bool ForwardingAudioStreamFactory::IsMuted() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return !!muter_;
-}
-
-void ForwardingAudioStreamFactory::AddLoopbackSink(
-    AudioStreamBroker::LoopbackSink* sink) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  loopback_sinks_.insert(sink);
-  web_contents()->IncrementCapturerCount(gfx::Size());
-}
-
-void ForwardingAudioStreamFactory::RemoveLoopbackSink(
-    AudioStreamBroker::LoopbackSink* sink) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  loopback_sinks_.erase(sink);
-  web_contents()->DecrementCapturerCount();
-}
-
-const base::UnguessableToken& ForwardingAudioStreamFactory::GetGroupID() {
-  return group_id_;
+  return is_muted_;
 }
 
 void ForwardingAudioStreamFactory::FrameDeleted(
     RenderFrameHost* render_frame_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(render_frame_host);
-  CleanupStreamsBelongingTo(render_frame_host);
+
+  // Unretained is safe since the destruction of |core_| will be posted to the
+  // IO thread later.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&Core::CleanupStreamsBelongingTo,
+                     base::Unretained(core_.get()),
+                     render_frame_host->GetProcess()->GetID(),
+                     render_frame_host->GetRoutingID()));
 }
 
-void ForwardingAudioStreamFactory::CleanupStreamsBelongingTo(
-    RenderFrameHost* render_frame_host) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(render_frame_host);
-
-  const int process_id = render_frame_host->GetProcess()->GetID();
-  const int frame_id = render_frame_host->GetRoutingID();
+void ForwardingAudioStreamFactory::Core::CleanupStreamsBelongingTo(
+    int render_process_id,
+    int render_frame_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   TRACE_EVENT_BEGIN2("audio", "CleanupStreamsBelongingTo", "group",
                      group_id_.GetLowForSerialization(), "process id",
-                     process_id);
+                     render_process_id);
 
   auto match_rfh =
-      [process_id,
-       frame_id](const std::unique_ptr<AudioStreamBroker>& broker) -> bool {
-    return broker->render_process_id() == process_id &&
-           broker->render_frame_id() == frame_id;
+      [render_process_id, render_frame_id](
+          const std::unique_ptr<AudioStreamBroker>& broker) -> bool {
+    return broker->render_process_id() == render_process_id &&
+           broker->render_frame_id() == render_frame_id;
   };
 
   base::EraseIf(outputs_, match_rfh);
@@ -207,27 +283,30 @@
 
   ResetRemoteFactoryPtrIfIdle();
 
-  TRACE_EVENT_END1("audio", "CleanupStreamsBelongingTo", "frame_id", frame_id);
+  TRACE_EVENT_END1("audio", "CleanupStreamsBelongingTo", "frame_id",
+                   render_frame_id);
 }
 
-void ForwardingAudioStreamFactory::RemoveInput(AudioStreamBroker* broker) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void ForwardingAudioStreamFactory::Core::RemoveInput(
+    AudioStreamBroker* broker) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   size_t removed = inputs_.erase(broker);
   DCHECK_EQ(1u, removed);
 
   ResetRemoteFactoryPtrIfIdle();
 }
 
-void ForwardingAudioStreamFactory::RemoveOutput(AudioStreamBroker* broker) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void ForwardingAudioStreamFactory::Core::RemoveOutput(
+    AudioStreamBroker* broker) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   size_t removed = outputs_.erase(broker);
   DCHECK_EQ(1u, removed);
 
   ResetRemoteFactoryPtrIfIdle();
 }
 
-audio::mojom::StreamFactory* ForwardingAudioStreamFactory::GetFactory() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+audio::mojom::StreamFactory* ForwardingAudioStreamFactory::Core::GetFactory() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!remote_factory_) {
     TRACE_EVENT_INSTANT1(
         "audio", "ForwardingAudioStreamFactory: Binding new factory",
@@ -235,9 +314,9 @@
     connector_->BindInterface(audio::mojom::kServiceName,
                               mojo::MakeRequest(&remote_factory_));
     // Unretained is safe because |this| owns |remote_factory_|.
-    remote_factory_.set_connection_error_handler(
-        base::BindOnce(&ForwardingAudioStreamFactory::ResetRemoteFactoryPtr,
-                       base::Unretained(this)));
+    remote_factory_.set_connection_error_handler(base::BindOnce(
+        &ForwardingAudioStreamFactory::Core::ResetRemoteFactoryPtr,
+        base::Unretained(this)));
 
     // Restore the muting session on reconnect.
     if (muter_)
@@ -247,14 +326,14 @@
   return remote_factory_.get();
 }
 
-void ForwardingAudioStreamFactory::ResetRemoteFactoryPtrIfIdle() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void ForwardingAudioStreamFactory::Core::ResetRemoteFactoryPtrIfIdle() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (inputs_.empty() && outputs_.empty())
     ResetRemoteFactoryPtr();
 }
 
-void ForwardingAudioStreamFactory::ResetRemoteFactoryPtr() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void ForwardingAudioStreamFactory::Core::ResetRemoteFactoryPtr() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (remote_factory_) {
     TRACE_EVENT_INSTANT1(
         "audio", "ForwardingAudioStreamFactory: Resetting factory",
diff --git a/content/browser/media/forwarding_audio_stream_factory.h b/content/browser/media/forwarding_audio_stream_factory.h
index e7b518f..f0c9e20 100644
--- a/content/browser/media/forwarding_audio_stream_factory.h
+++ b/content/browser/media/forwarding_audio_stream_factory.h
@@ -5,12 +5,14 @@
 #ifndef CONTENT_BROWSER_MEDIA_FORWARDING_AUDIO_STREAM_FACTORY_H_
 #define CONTENT_BROWSER_MEDIA_FORWARDING_AUDIO_STREAM_FACTORY_H_
 
+#include <cstdint>
 #include <memory>
 #include <string>
 
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/unguessable_token.h"
 #include "content/browser/media/audio_muting_session.h"
@@ -18,6 +20,8 @@
 #include "content/common/content_export.h"
 #include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "media/mojo/interfaces/audio_output_stream.mojom.h"
+#include "services/audio/public/mojom/audio_processing.mojom.h"
 #include "services/audio/public/mojom/stream_factory.mojom.h"
 
 namespace service_manager {
@@ -26,22 +30,150 @@
 
 namespace media {
 class AudioParameters;
+class UserInputMonitorBase;
 }
 
 namespace content {
 
-class AudioStreamBroker;
 class RenderFrameHost;
 class WebContents;
 
 // This class handles stream creation operations for a WebContents.
 // This class is operated on the UI thread.
 class CONTENT_EXPORT ForwardingAudioStreamFactory final
-    : public WebContentsObserver,
-      public AudioStreamBroker::LoopbackSource {
+    : public WebContentsObserver {
  public:
+  // Note: all methods of Core may only be called on the IO thread except for
+  // the constructor. The destruction of Core is posted to the IO thread when
+  // the owning ForwardingAudioStreamFactory is destructed, so any task posted
+  // to the IO thread while the ForwardingAudioStreamFactory is alive may post
+  // |core()| using base::Unretained.
+  class CONTENT_EXPORT Core final : public AudioStreamBroker::LoopbackSource {
+   public:
+    Core(base::WeakPtr<ForwardingAudioStreamFactory> owner,
+         media::UserInputMonitorBase* user_input_monitor,
+         std::unique_ptr<service_manager::Connector> connector,
+         std::unique_ptr<AudioStreamBrokerFactory> factory);
+    ~Core() final;
+
+    const base::UnguessableToken& group_id() const { return group_id_; }
+
+    // E.g. to override binder.
+    service_manager::Connector* get_connector_for_testing() {
+      return connector_.get();
+    }
+
+    // TODO(https://crbug.com/787806): Automatically restore streams on audio
+    // service restart.
+    void CreateInputStream(
+        int render_process_id,
+        int render_frame_id,
+        const std::string& device_id,
+        const media::AudioParameters& params,
+        uint32_t shared_memory_count,
+        bool enable_agc,
+        audio::mojom::AudioProcessingConfigPtr processing_config,
+        mojom::RendererAudioInputStreamFactoryClientPtr
+            renderer_factory_client);
+
+    void AssociateInputAndOutputForAec(
+        const base::UnguessableToken& input_stream_id,
+        const std::string& raw_output_device_id);
+
+    void CreateOutputStream(
+        int render_process_id,
+        int render_frame_id,
+        const std::string& device_id,
+        const media::AudioParameters& params,
+        const base::Optional<base::UnguessableToken>& processing_id,
+        media::mojom::AudioOutputStreamProviderClientPtr client);
+
+    void CreateLoopbackStream(
+        int render_process_id,
+        int render_frame_id,
+        AudioStreamBroker::LoopbackSource* loopback_source,
+        const media::AudioParameters& params,
+        uint32_t shared_memory_count,
+        bool mute_source,
+        mojom::RendererAudioInputStreamFactoryClientPtr
+            renderer_factory_client);
+
+    // Sets the muting state for all output streams created through this
+    // factory.
+    void SetMuted(bool muted);
+
+    // AudioStreamLoopback::Source implementation
+    void AddLoopbackSink(AudioStreamBroker::LoopbackSink* sink) final;
+    void RemoveLoopbackSink(AudioStreamBroker::LoopbackSink* sink) final;
+    const base::UnguessableToken& GetGroupID() final;  // Actually const.
+
+   private:
+    // For CleanupStreamsBelongingTo.
+    friend class ForwardingAudioStreamFactory;
+
+    using StreamBrokerSet = base::flat_set<std::unique_ptr<AudioStreamBroker>,
+                                           base::UniquePtrComparator>;
+
+    void CleanupStreamsBelongingTo(int render_process_id, int render_frame_id);
+
+    void RemoveInput(AudioStreamBroker* handle);
+    void RemoveOutput(AudioStreamBroker* handle);
+
+    audio::mojom::StreamFactory* GetFactory();
+    void ResetRemoteFactoryPtrIfIdle();
+    void ResetRemoteFactoryPtr();
+
+    media::UserInputMonitorBase* const user_input_monitor_;
+
+    // Used for posting tasks the UI thread to communicate when a loopback
+    // stream is started/stopped. Weak since |this| on the IO thread outlives
+    // |owner| on the UI thread.
+    const base::WeakPtr<ForwardingAudioStreamFactory> owner_;
+
+    const std::unique_ptr<AudioStreamBrokerFactory> broker_factory_;
+
+    // Unique id identifying all streams belonging to the WebContents owning
+    // |this|.
+    const base::UnguessableToken group_id_;
+
+    const std::unique_ptr<service_manager::Connector> connector_;
+
+    // Lazily acquired. Reset on connection error and when we no longer have any
+    // streams. Note: we don't want muting to force the connection to be open,
+    // since we want to clean up the service when not in use. If we have active
+    // muting but nothing else, we should stop it and start it again when we
+    // need to reacquire the factory for some other reason.
+    audio::mojom::StreamFactoryPtr remote_factory_;
+
+    // Running id used for tracking audible streams. We keep count here to avoid
+    // collisions.
+    // TODO(https://crbug.com/830494): Refactor to make this unnecessary and
+    // remove it.
+    int stream_id_counter_ = 0;
+
+    // Instantiated when |outputs_| should be muted, empty otherwise.
+    base::Optional<AudioMutingSession> muter_;
+
+    StreamBrokerSet inputs_;
+    StreamBrokerSet outputs_;
+    base::flat_set<AudioStreamBroker::LoopbackSink*> loopback_sinks_;
+    DISALLOW_COPY_AND_ASSIGN(Core);
+  };
+
+  // Returns the ForwardingAudioStreamFactory which takes care of stream
+  // creation for |frame|. Returns null if |frame| is null or if the frame
+  // doesn't belong to a WebContents.
+  static ForwardingAudioStreamFactory* ForFrame(RenderFrameHost* frame);
+
+  // Returns the ForwardingAudioStreamFactory::Core which takes care of stream
+  // creation for |frame|. Returns null if |frame| is null or if the frame
+  // doesn't belong to a WebContents.
+  static ForwardingAudioStreamFactory::Core* CoreForFrame(
+      RenderFrameHost* frame);
+
   // |web_contents| is null in the browser-privileged access case, i.e., when
   // the streams created with this factory will not be consumed by a renderer.
+  // |connector| will be used on the IO thread.
   ForwardingAudioStreamFactory(
       WebContents* web_contents,
       media::UserInputMonitorBase* user_input_monitor,
@@ -50,42 +182,14 @@
 
   ~ForwardingAudioStreamFactory() final;
 
-  // Returns the ForwardingAudioStreamFactory which takes care of stream
-  // creation for |frame|. Returns null if |frame| is null or if the frame
-  // doesn't belong to a WebContents.
-  static ForwardingAudioStreamFactory* ForFrame(RenderFrameHost* frame);
+  const base::UnguessableToken& group_id() const {
+    // Thread-safe since Core::group_id is thread-safe and |core_| outlives
+    // |this|.
+    return core_->group_id();
+  }
 
-  const base::UnguessableToken& group_id() { return group_id_; }
-
-  // TODO(https://crbug.com/787806): Automatically restore streams on audio
-  // service restart.
-  void CreateInputStream(
-      RenderFrameHost* frame,
-      const std::string& device_id,
-      const media::AudioParameters& params,
-      uint32_t shared_memory_count,
-      bool enable_agc,
-      audio::mojom::AudioProcessingConfigPtr processing_config,
-      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client);
-
-  void AssociateInputAndOutputForAec(
-      const base::UnguessableToken& input_stream_id,
-      const std::string& raw_output_device_id);
-
-  void CreateOutputStream(
-      RenderFrameHost* frame,
-      const std::string& device_id,
-      const media::AudioParameters& params,
-      const base::Optional<base::UnguessableToken>& processing_id,
-      media::mojom::AudioOutputStreamProviderClientPtr client);
-
-  void CreateLoopbackStream(
-      RenderFrameHost* frame,
-      ForwardingAudioStreamFactory* loopback_source,
-      const media::AudioParameters& params,
-      uint32_t shared_memory_count,
-      bool mute_source,
-      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client);
+  void LoopbackStreamStarted();
+  void LoopbackStreamStopped();
 
   // Sets the muting state for all output streams created through this factory.
   void SetMuted(bool muted);
@@ -93,62 +197,17 @@
   // Returns the current muting state.
   bool IsMuted() const;
 
-  // AudioStreamLoopback::Source implementation
-  void AddLoopbackSink(AudioStreamBroker::LoopbackSink* sink) final;
-  void RemoveLoopbackSink(AudioStreamBroker::LoopbackSink* sink) final;
-  const base::UnguessableToken& GetGroupID() final;
-
   // WebContentsObserver implementation. We observe these events so that we can
   // clean up streams belonging to a frame when that frame is destroyed.
   void FrameDeleted(RenderFrameHost* render_frame_host) final;
 
-  // E.g. to override binder.
-  service_manager::Connector* get_connector_for_testing() {
-    return connector_.get();
-  }
+  Core* core() { return core_.get(); }
 
  private:
-  using StreamBrokerSet = base::flat_set<std::unique_ptr<AudioStreamBroker>,
-                                         base::UniquePtrComparator>;
+  std::unique_ptr<Core> core_;
+  bool is_muted_ = false;
 
-  void CleanupStreamsBelongingTo(RenderFrameHost* render_frame_host);
-
-  void RemoveInput(AudioStreamBroker* handle);
-  void RemoveOutput(AudioStreamBroker* handle);
-
-  audio::mojom::StreamFactory* GetFactory();
-  void ResetRemoteFactoryPtrIfIdle();
-  void ResetRemoteFactoryPtr();
-
-  media::UserInputMonitorBase* const user_input_monitor_;
-
-  const std::unique_ptr<service_manager::Connector> connector_;
-  const std::unique_ptr<AudioStreamBrokerFactory> broker_factory_;
-
-  // Unique id indentifying all streams belonging to the WebContents owning
-  // |this|.
-  // TODO(https://crbug.com/824019): Use this for loopback.
-  const base::UnguessableToken group_id_;
-
-  // Lazily acquired. Reset on connection error and when we no longer have any
-  // streams. Note: we don't want muting to force the connection to be open,
-  // since we want to clean up the service when not in use. If we have active
-  // muting but nothing else, we should stop it and start it again when we need
-  // to reacquire the factory for some other reason.
-  audio::mojom::StreamFactoryPtr remote_factory_;
-
-  // Running id used for tracking audible streams. We keep count here to avoid
-  // collisions.
-  // TODO(https://crbug.com/830494): Refactor to make this unnecessary and
-  // remove it.
-  int stream_id_counter_ = 0;
-
-  // Instantiated when |outputs_| should be muted, empty otherwise.
-  base::Optional<AudioMutingSession> muter_;
-
-  StreamBrokerSet inputs_;
-  StreamBrokerSet outputs_;
-  base::flat_set<AudioStreamBroker::LoopbackSink*> loopback_sinks_;
+  base::WeakPtrFactory<ForwardingAudioStreamFactory> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ForwardingAudioStreamFactory);
 };
diff --git a/content/browser/media/forwarding_audio_stream_factory_unittest.cc b/content/browser/media/forwarding_audio_stream_factory_unittest.cc
index 0fcc2ce6..d390f5817 100644
--- a/content/browser/media/forwarding_audio_stream_factory_unittest.cc
+++ b/content/browser/media/forwarding_audio_stream_factory_unittest.cc
@@ -272,9 +272,10 @@
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
-                            kSharedMemoryCount, kEnableAgc, nullptr,
-                            std::move(client));
+  factory.core()->CreateInputStream(main_rfh()->GetProcess()->GetID(),
+                                    main_rfh()->GetRoutingID(), kInputDeviceId,
+                                    kParams, kSharedMemoryCount, kEnableAgc,
+                                    nullptr, std::move(client));
 }
 
 TEST_F(ForwardingAudioStreamFactoryTest,
@@ -284,19 +285,23 @@
   base::WeakPtr<MockBroker> broker =
       ExpectLoopbackBrokerConstruction(main_rfh());
 
+  std::unique_ptr<service_manager::Connector> other_connector =
+      connector_->Clone();
+
   ForwardingAudioStreamFactory factory(
       web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
       std::move(broker_factory_));
 
   ForwardingAudioStreamFactory source_factory(
       source_contents.get(), nullptr /*user_input_monitor*/,
-      std::move(connector_), std::make_unique<MockBrokerFactory>());
+      std::move(other_connector), std::make_unique<MockBrokerFactory>());
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateLoopbackStream(main_rfh(), &source_factory, kParams,
-                               kSharedMemoryCount, kMuteSource,
-                               std::move(client));
+  factory.core()->CreateLoopbackStream(
+      main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+      source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
+      std::move(client));
 }
 
 TEST_F(ForwardingAudioStreamFactoryTest,
@@ -310,8 +315,9 @@
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                             base::nullopt, std::move(client));
+  factory.core()->CreateOutputStream(
+      main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+      kOutputDeviceId, kParams, base::nullopt, std::move(client));
 }
 
 TEST_F(ForwardingAudioStreamFactoryTest,
@@ -329,17 +335,19 @@
   {
     EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(client));
+    factory.core()->CreateInputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(client));
     testing::Mock::VerifyAndClear(&*main_rfh_broker);
   }
   {
     EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(client));
+    factory.core()->CreateInputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(client));
     testing::Mock::VerifyAndClear(&*other_rfh_broker);
   }
 
@@ -358,28 +366,33 @@
   base::WeakPtr<MockBroker> other_rfh_broker =
       ExpectLoopbackBrokerConstruction(other_rfh());
 
+  std::unique_ptr<service_manager::Connector> other_connector =
+      connector_->Clone();
+
   ForwardingAudioStreamFactory factory(
       web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
       std::move(broker_factory_));
 
   ForwardingAudioStreamFactory source_factory(
       source_contents.get(), nullptr /*user_input_monitor*/,
-      std::move(connector_), std::make_unique<MockBrokerFactory>());
+      std::move(other_connector), std::make_unique<MockBrokerFactory>());
 
   {
     EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateLoopbackStream(main_rfh(), &source_factory, kParams,
-                                 kSharedMemoryCount, kMuteSource,
-                                 std::move(client));
+    factory.core()->CreateLoopbackStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
+        std::move(client));
     testing::Mock::VerifyAndClear(&*main_rfh_broker);
   }
   {
     EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateLoopbackStream(other_rfh(), &source_factory, kParams,
-                                 kSharedMemoryCount, kMuteSource,
-                                 std::move(client));
+    factory.core()->CreateLoopbackStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
+        std::move(client));
     testing::Mock::VerifyAndClear(&*other_rfh_broker);
   }
 
@@ -404,15 +417,17 @@
   {
     EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(client));
+    factory.core()->CreateOutputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(client));
     testing::Mock::VerifyAndClear(&*main_rfh_broker);
   }
   {
     EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(client));
+    factory.core()->CreateOutputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(client));
     testing::Mock::VerifyAndClear(&*other_rfh_broker);
   }
 
@@ -442,60 +457,69 @@
   base::WeakPtr<MockBroker> other_rfh_output_broker =
       ExpectOutputBrokerConstruction(other_rfh());
 
+  std::unique_ptr<service_manager::Connector> other_connector =
+      connector_->Clone();
+
   ForwardingAudioStreamFactory factory(
       web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
       std::move(broker_factory_));
 
   ForwardingAudioStreamFactory source_factory(
       source_contents.get(), nullptr /*user_input_monitor*/,
-      std::move(connector_), std::make_unique<MockBrokerFactory>());
+      std::move(other_connector), std::make_unique<MockBrokerFactory>());
 
   {
     EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(input_client));
+    factory.core()->CreateInputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
   }
   {
     EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(input_client));
+    factory.core()->CreateInputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
   }
 
   {
     EXPECT_CALL(*main_rfh_loopback_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateLoopbackStream(main_rfh(), &source_factory, kParams,
-                                 kSharedMemoryCount, kMuteSource,
-                                 std::move(input_client));
+    factory.core()->CreateLoopbackStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*main_rfh_loopback_broker);
   }
   {
     EXPECT_CALL(*other_rfh_loopback_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateLoopbackStream(other_rfh(), &source_factory, kParams,
-                                 kSharedMemoryCount, kMuteSource,
-                                 std::move(input_client));
+    factory.core()->CreateLoopbackStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*other_rfh_loopback_broker);
   }
 
   {
     EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&output_client);
-    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(output_client));
+    factory.core()->CreateOutputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
     testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
   }
   {
     EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&output_client);
-    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(output_client));
+    factory.core()->CreateOutputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
     testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
   }
 
@@ -528,14 +552,16 @@
 
   EXPECT_CALL(*input_broker, CreateStream(NotNull()));
   mojo::MakeRequest(&input_client);
-  factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
-                            kSharedMemoryCount, kEnableAgc, nullptr,
-                            std::move(input_client));
+  factory.core()->CreateInputStream(main_rfh()->GetProcess()->GetID(),
+                                    main_rfh()->GetRoutingID(), kInputDeviceId,
+                                    kParams, kSharedMemoryCount, kEnableAgc,
+                                    nullptr, std::move(input_client));
 
   EXPECT_CALL(*output_broker, CreateStream(NotNull()));
   mojo::MakeRequest(&output_client);
-  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                             base::nullopt, std::move(output_client));
+  factory.core()->CreateOutputStream(
+      main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+      kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
 
   DeleteContents();
   base::RunLoop().RunUntilIdle();
@@ -566,32 +592,36 @@
   {
     EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(input_client));
+    factory.core()->CreateInputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
   }
   {
     EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&input_client);
-    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
-                              kSharedMemoryCount, kEnableAgc, nullptr,
-                              std::move(input_client));
+    factory.core()->CreateInputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
+        std::move(input_client));
     testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
   }
 
   {
     EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&output_client);
-    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(output_client));
+    factory.core()->CreateOutputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
     testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
   }
   {
     EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&output_client);
-    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(output_client));
+    factory.core()->CreateOutputStream(
+        other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
     testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
   }
 
@@ -644,8 +674,9 @@
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                             base::nullopt, std::move(client));
+  factory.core()->CreateOutputStream(
+      main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+      kOutputDeviceId, kParams, base::nullopt, std::move(client));
   base::RunLoop().RunUntilIdle();
   testing::Mock::VerifyAndClear(&*broker);
 
@@ -684,8 +715,9 @@
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                             base::nullopt, std::move(client));
+  factory.core()->CreateOutputStream(
+      main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+      kOutputDeviceId, kParams, base::nullopt, std::move(client));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(factory.IsMuted());
   EXPECT_TRUE(stream_factory_.IsConnected());
@@ -713,8 +745,9 @@
   {
     EXPECT_CALL(*broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(client));
+    factory.core()->CreateOutputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(client));
     base::RunLoop().RunUntilIdle();
     testing::Mock::VerifyAndClear(&*broker);
   }
@@ -730,8 +763,9 @@
   {
     EXPECT_CALL(*another_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
-                               base::nullopt, std::move(client));
+    factory.core()->CreateOutputStream(
+        main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
+        kOutputDeviceId, kParams, base::nullopt, std::move(client));
     base::RunLoop().RunUntilIdle();
     testing::Mock::VerifyAndClear(&*another_broker);
   }
@@ -762,10 +796,11 @@
         web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
         std::move(broker_factory_));
 
-    factory.AddLoopbackSink(&sink1);
-    factory.AddLoopbackSink(&sink2);
-    factory.RemoveLoopbackSink(&sink1);
+    factory.core()->AddLoopbackSink(&sink1);
+    factory.core()->AddLoopbackSink(&sink2);
+    factory.core()->RemoveLoopbackSink(&sink1);
   }
+  base::RunLoop().RunUntilIdle();
 }
 
 }  // namespace content
diff --git a/content/browser/media/in_process_audio_loopback_stream_creator.cc b/content/browser/media/in_process_audio_loopback_stream_creator.cc
index ad26dac..12b51f3 100644
--- a/content/browser/media/in_process_audio_loopback_stream_creator.cc
+++ b/content/browser/media/in_process_audio_loopback_stream_creator.cc
@@ -7,8 +7,14 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/common/service_manager_connection.h"
@@ -53,6 +59,39 @@
   DISALLOW_COPY_AND_ASSIGN(StreamCreatedCallbackAdapter);
 };
 
+void CreateLoopbackStreamHelper(
+    ForwardingAudioStreamFactory::Core* factory,
+    AudioStreamBroker::LoopbackSource* loopback_source,
+    const media::AudioParameters& params,
+    uint32_t total_segments,
+    mojom::RendererAudioInputStreamFactoryClientPtrInfo client_ptr_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  const bool mute_source = true;
+  mojom::RendererAudioInputStreamFactoryClientPtr client;
+  client.Bind(std::move(client_ptr_info));
+
+  factory->CreateLoopbackStream(-1, -1, loopback_source, params, total_segments,
+                                mute_source, std::move(client));
+}
+
+void CreateSystemWideLoopbackStreamHelper(
+    ForwardingAudioStreamFactory::Core* factory,
+    const media::AudioParameters& params,
+    uint32_t total_segments,
+    mojom::RendererAudioInputStreamFactoryClientPtrInfo client_ptr_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  const bool enable_agc = false;
+  mojom::RendererAudioInputStreamFactoryClientPtr client;
+  client.Bind(std::move(client_ptr_info));
+
+  factory->CreateInputStream(
+      -1, -1, media::AudioDeviceDescription::kLoopbackWithMuteDeviceId, params,
+      total_segments, enable_agc, nullptr /* processing_config */,
+      std::move(client));
+}
+
 }  // namespace
 
 InProcessAudioLoopbackStreamCreator::InProcessAudioLoopbackStreamCreator()
@@ -78,22 +117,27 @@
     uint32_t total_segments,
     const StreamCreatedCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  mojom::RendererAudioInputStreamFactoryClientPtr client;
+  mojom::RendererAudioInputStreamFactoryClientPtrInfo client;
   mojo::MakeStrongBinding(
       std::make_unique<StreamCreatedCallbackAdapter>(callback),
       mojo::MakeRequest(&client));
+  // Deletion of factory_.core() is posted to the IO thread when |factory_| is
+  // destroyed, so Unretained is safe below.
   if (loopback_source) {
-    factory_.CreateLoopbackStream(
-        nullptr,
-        static_cast<WebContentsImpl*>(loopback_source)->GetAudioStreamFactory(),
-        params, total_segments, true /* mute_source */, std::move(client));
-  } else {
-    // A null |frame_of_source_web_contents| requests system-wide loopback.
-    factory_.CreateInputStream(
-        nullptr, media::AudioDeviceDescription::kLoopbackWithMuteDeviceId,
-        params, total_segments, false /* enable_agc */,
-        nullptr /* processing_config */, std::move(client));
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&CreateLoopbackStreamHelper, factory_.core(),
+                       static_cast<WebContentsImpl*>(loopback_source)
+                           ->GetAudioStreamFactory()
+                           ->core(),
+                       params, total_segments, std::move(client)));
+    return;
   }
+  // A null |frame_of_source_web_contents| requests system-wide loopback.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&CreateSystemWideLoopbackStreamHelper, factory_.core(),
+                     params, total_segments, std::move(client)));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
index e92b04b6..e7e8b7a 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
@@ -4,15 +4,25 @@
 
 #include "content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h"
 
+#include <cstdint>
 #include <string>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
 #include "base/task/post_task.h"
 #include "base/trace_event/trace_event.h"
-#include "content/browser/browser_main_loop.h"
+#include "base/unguessable_token.h"
+#include "content/browser/media/audio_stream_broker.h"
 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
 #include "content/browser/media/forwarding_audio_stream_factory.h"
 #include "content/browser/media/media_devices_permission_checker.h"
+#include "content/browser/media/media_devices_util.h"
+#include "content/browser/renderer_host/media/audio_input_device_manager.h"
+#include "content/browser/renderer_host/media/media_devices_manager.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -22,25 +32,29 @@
 #include "content/public/browser/web_contents_media_capture_id.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/audio/audio_device_description.h"
-#include "media/audio/audio_input_device.h"
 #include "media/base/audio_parameters.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/audio/public/mojom/audio_processing.mojom.h"
+#include "url/origin.h"
 
 namespace content {
 
 namespace {
 
-void LookUpDeviceAndRespondIfFound(
-    scoped_refptr<AudioInputDeviceManager> audio_input_device_manager,
-    int32_t session_id,
-    base::OnceCallback<void(const MediaStreamDevice&)> response) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  const MediaStreamDevice* device =
-      audio_input_device_manager->GetOpenedDeviceById(session_id);
-  if (device) {
-    // Copies device.
-    base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
-                             base::BindOnce(std::move(response), *device));
+AudioStreamBroker::LoopbackSource* GetLoopbackSourceOnUIThread(
+    int render_process_id,
+    int render_frame_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* source = ForwardingAudioStreamFactory::CoreForFrame(
+      (RenderFrameHost::FromID(render_process_id, render_frame_id)));
+  if (!source) {
+    // The source of the capture has already been destroyed, so fail early.
+    return nullptr;
   }
+  // Note: this pointer is sent over to the IO thread. This is safe since the
+  // destruction of |source| is posted to the IO thread and it hasn't been
+  // posted yet.
+  return source;
 }
 
 void EnumerateOutputDevices(MediaStreamManager* media_stream_manager,
@@ -61,75 +75,164 @@
     if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
             salt_and_origin.device_id_salt, salt_and_origin.origin, device_id,
             device_info.device_id)) {
-      base::PostTaskWithTraits(
-          FROM_HERE, {BrowserThread::UI},
-          base::BindOnce(std::move(cb), device_info.device_id));
+      cb.Run(device_info.device_id);
       break;
     }
   }
   // If we're unable to translate the device id, |cb| will not be run.
 }
 
+void GetSaltOriginAndPermissionsOnUIThread(
+    int process_id,
+    int frame_id,
+    base::OnceCallback<void(MediaDeviceSaltAndOrigin salt_and_origin,
+                            bool has_access)> cb) {
+  auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
+  bool access = MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
+      MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id);
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
+}
+
 }  // namespace
 
+class RenderFrameAudioInputStreamFactory::Core final
+    : public mojom::RendererAudioInputStreamFactory {
+ public:
+  Core(mojom::RendererAudioInputStreamFactoryRequest request,
+       MediaStreamManager* media_stream_manager,
+       RenderFrameHost* render_frame_host);
+
+  ~Core() final;
+
+  void Init(mojom::RendererAudioInputStreamFactoryRequest request);
+
+  // mojom::RendererAudioInputStreamFactory implementation.
+  void CreateStream(
+      mojom::RendererAudioInputStreamFactoryClientPtr client,
+      int32_t session_id,
+      const media::AudioParameters& audio_params,
+      bool automatic_gain_control,
+      uint32_t shared_memory_count,
+      audio::mojom::AudioProcessingConfigPtr processing_config) final;
+
+  void AssociateInputAndOutputForAec(
+      const base::UnguessableToken& input_stream_id,
+      const std::string& output_device_id) final;
+
+  void CreateLoopbackStream(
+      mojom::RendererAudioInputStreamFactoryClientPtr client,
+      const media::AudioParameters& audio_params,
+      uint32_t shared_memory_count,
+      bool disable_local_echo,
+      AudioStreamBroker::LoopbackSource* loopback_source);
+
+  void AssociateInputAndOutputForAecAfterCheckingAccess(
+      const base::UnguessableToken& input_stream_id,
+      const std::string& output_device_id,
+      MediaDeviceSaltAndOrigin salt_and_origin,
+      bool access_granted);
+
+  void AssociateTranslatedOutputDeviceForAec(
+      const base::UnguessableToken& input_stream_id,
+      const std::string& raw_output_device_id);
+
+  MediaStreamManager* const media_stream_manager_;
+  const int process_id_;
+  const int frame_id_;
+  const url::Origin origin_;
+
+  mojo::Binding<RendererAudioInputStreamFactory> binding_;
+  ForwardingAudioStreamFactory::Core* forwarding_factory_;
+
+  base::WeakPtrFactory<Core> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
 RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
     mojom::RendererAudioInputStreamFactoryRequest request,
-    scoped_refptr<AudioInputDeviceManager> audio_input_device_manager,
+    MediaStreamManager* media_stream_manager,
     RenderFrameHost* render_frame_host)
-    : binding_(this, std::move(request)),
-      audio_input_device_manager_(std::move(audio_input_device_manager)),
-      render_frame_host_(render_frame_host),
-      weak_ptr_factory_(this) {
+    : core_(new Core(std::move(request),
+                     media_stream_manager,
+                     render_frame_host)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 RenderFrameAudioInputStreamFactory::~RenderFrameAudioInputStreamFactory() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
+  // as it doesn't post in case it is already executed on the right thread. That
+  // causes issues in unit tests where the UI thread and the IO thread are the
+  // same.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
 }
 
-void RenderFrameAudioInputStreamFactory::CreateStream(
+RenderFrameAudioInputStreamFactory::Core::Core(
+    mojom::RendererAudioInputStreamFactoryRequest request,
+    MediaStreamManager* media_stream_manager,
+    RenderFrameHost* render_frame_host)
+    : media_stream_manager_(media_stream_manager),
+      process_id_(render_frame_host->GetProcess()->GetID()),
+      frame_id_(render_frame_host->GetRoutingID()),
+      origin_(render_frame_host->GetLastCommittedOrigin()),
+      binding_(this),
+      forwarding_factory_(
+          ForwardingAudioStreamFactory::CoreForFrame(render_frame_host)),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!forwarding_factory_) {
+    // The only case when we not have a forwarding factory is when the
+    // frame belongs to an interstitial. Interstitials don't need audio, so it's
+    // fine to drop the request.
+    return;
+  }
+
+  // Unretained is safe since the destruction of |this| is posted to the IO
+  // thread.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&Core::Init, base::Unretained(this), std::move(request)));
+}
+
+RenderFrameAudioInputStreamFactory::Core::~Core() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+void RenderFrameAudioInputStreamFactory::Core::Init(
+    mojom::RendererAudioInputStreamFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  binding_.Bind(std::move(request));
+}
+
+void RenderFrameAudioInputStreamFactory::Core::CreateStream(
     mojom::RendererAudioInputStreamFactoryClientPtr client,
     int32_t session_id,
     const media::AudioParameters& audio_params,
     bool automatic_gain_control,
     uint32_t shared_memory_count,
     audio::mojom::AudioProcessingConfigPtr processing_config) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  TRACE_EVENT_INSTANT1("audio",
-                       "RenderFrameAudioInputStreamFactory::CreateStream",
-                       TRACE_EVENT_SCOPE_THREAD, "session id", session_id);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
+  TRACE_EVENT1("audio", "RenderFrameAudioInputStreamFactory::CreateStream",
+               "session id", session_id);
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(
-          &LookUpDeviceAndRespondIfFound, audio_input_device_manager_,
-          session_id,
-          base::BindOnce(&RenderFrameAudioInputStreamFactory::
-                             CreateStreamAfterLookingUpDevice,
-                         weak_ptr_factory_.GetWeakPtr(), std::move(client),
-                         audio_params, automatic_gain_control,
-                         shared_memory_count, std::move(processing_config))));
-}
+  const MediaStreamDevice* device =
+      media_stream_manager_->audio_input_device_manager()->GetOpenedDeviceById(
+          session_id);
 
-void RenderFrameAudioInputStreamFactory::CreateStreamAfterLookingUpDevice(
-    mojom::RendererAudioInputStreamFactoryClientPtr client,
-    const media::AudioParameters& audio_params,
-    bool automatic_gain_control,
-    uint32_t shared_memory_count,
-    audio::mojom::AudioProcessingConfigPtr processing_config,
-    const MediaStreamDevice& device) {
-  TRACE_EVENT1(
-      "audio",
-      "RenderFrameAudioInputStreamFactory::CreateStreamAfterLookingUpDevice",
-      "device id", device.id);
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ForwardingAudioStreamFactory* factory =
-      ForwardingAudioStreamFactory::ForFrame(render_frame_host_);
-  if (!factory)
+  if (!device) {
+    TRACE_EVENT_INSTANT0("audio", "device not found", TRACE_EVENT_SCOPE_THREAD);
     return;
+  }
 
   WebContentsMediaCaptureId capture_id;
-  if (WebContentsMediaCaptureId::Parse(device.id, &capture_id)) {
+  if (WebContentsMediaCaptureId::Parse(device->id, &capture_id)) {
     // For MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, the source is selected from
     // picker window, we do not mute the source audio. For
     // MEDIA_GUM_TAB_AUDIO_CAPTURE, the probable use case is Cast, we mute
@@ -137,85 +240,102 @@
     // TODO(qiangchen): Analyze audio constraints to make a duplicating or
     // diverting decision. It would give web developer more flexibility.
 
-    ForwardingAudioStreamFactory* loopback_source =
-        ForwardingAudioStreamFactory::ForFrame((RenderFrameHost::FromID(
-            capture_id.render_process_id, capture_id.main_render_frame_id)));
-    if (!loopback_source) {
-      // The source of the capture has already been destroyed, so fail early.
-      return;
-    }
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(&GetLoopbackSourceOnUIThread,
+                       capture_id.render_process_id,
+                       capture_id.main_render_frame_id),
+        base::BindOnce(
+            &RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream,
+            weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
+            shared_memory_count, capture_id.disable_local_echo));
 
-    factory->CreateLoopbackStream(
-        render_frame_host_, loopback_source, audio_params, shared_memory_count,
-        capture_id.disable_local_echo, std::move(client));
-
-    if (device.type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
+    if (device->type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
+    return;
   } else {
-    factory->CreateInputStream(render_frame_host_, device.id, audio_params,
-                               shared_memory_count, automatic_gain_control,
-                               std::move(processing_config), std::move(client));
+    forwarding_factory_->CreateInputStream(
+        process_id_, frame_id_, device->id, audio_params, shared_memory_count,
+        automatic_gain_control, std::move(processing_config),
+        std::move(client));
 
     // Only count for captures from desktop media picker dialog and system loop
     // back audio.
-    if (device.type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
-        (media::AudioDeviceDescription::IsLoopbackDevice(device.id))) {
+    if (device->type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
+        (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
     }
   }
 }
 
-void RenderFrameAudioInputStreamFactory::AssociateInputAndOutputForAec(
+void RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream(
+    mojom::RendererAudioInputStreamFactoryClientPtr client,
+    const media::AudioParameters& audio_params,
+    uint32_t shared_memory_count,
+    bool disable_local_echo,
+    AudioStreamBroker::LoopbackSource* loopback_source) {
+  if (!loopback_source)
+    return;
+
+  forwarding_factory_->CreateLoopbackStream(
+      process_id_, frame_id_, loopback_source, audio_params,
+      shared_memory_count, disable_local_echo, std::move(client));
+}
+
+void RenderFrameAudioInputStreamFactory::Core::AssociateInputAndOutputForAec(
     const base::UnguessableToken& input_stream_id,
     const std::string& output_device_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
   if (!IsValidDeviceId(output_device_id))
     return;
 
-  ForwardingAudioStreamFactory* factory =
-      ForwardingAudioStreamFactory::ForFrame(render_frame_host_);
-  if (!factory)
-    return;
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(
+          &GetSaltOriginAndPermissionsOnUIThread, process_id_, frame_id_,
+          base::BindOnce(
+              &Core::AssociateInputAndOutputForAecAfterCheckingAccess,
+              weak_ptr_factory_.GetWeakPtr(), input_stream_id,
+              output_device_id)));
+}
 
-  const int process_id = render_frame_host_->GetProcess()->GetID();
-  const int frame_id = render_frame_host_->GetRoutingID();
-  auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
+void RenderFrameAudioInputStreamFactory::Core::
+    AssociateInputAndOutputForAecAfterCheckingAccess(
+        const base::UnguessableToken& input_stream_id,
+        const std::string& output_device_id,
+        MediaDeviceSaltAndOrigin salt_and_origin,
+        bool access_granted) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
 
-  // Check permissions for everything but the default device
-  if (!media::AudioDeviceDescription::IsDefaultDevice(output_device_id) &&
-      !MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
-          MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id)) {
+  if (!access_granted)
     return;
-  }
 
   if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id) ||
       media::AudioDeviceDescription::IsCommunicationsDevice(output_device_id)) {
-    factory->AssociateInputAndOutputForAec(input_stream_id, output_device_id);
+    forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
+                                                       output_device_id);
   } else {
-    auto* media_stream_manager =
-        BrowserMainLoop::GetInstance()->media_stream_manager();
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(
-            EnumerateOutputDevices, media_stream_manager,
-            base::BindRepeating(
-                &TranslateDeviceId, output_device_id, salt_and_origin,
-                base::BindRepeating(&RenderFrameAudioInputStreamFactory::
-                                        AssociateTranslatedOutputDeviceForAec,
-                                    weak_ptr_factory_.GetWeakPtr(),
-                                    input_stream_id))));
+    EnumerateOutputDevices(
+        media_stream_manager_,
+        base::BindRepeating(
+            &TranslateDeviceId, output_device_id, salt_and_origin,
+            base::BindRepeating(&RenderFrameAudioInputStreamFactory::Core::
+                                    AssociateTranslatedOutputDeviceForAec,
+                                weak_ptr_factory_.GetWeakPtr(),
+                                input_stream_id)));
   }
 }
 
-void RenderFrameAudioInputStreamFactory::AssociateTranslatedOutputDeviceForAec(
-    const base::UnguessableToken& input_stream_id,
-    const std::string& raw_output_device_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ForwardingAudioStreamFactory* factory =
-      ForwardingAudioStreamFactory::ForFrame(render_frame_host_);
-  if (factory)
-    factory->AssociateInputAndOutputForAec(input_stream_id,
-                                           raw_output_device_id);
+void RenderFrameAudioInputStreamFactory::Core::
+    AssociateTranslatedOutputDeviceForAec(
+        const base::UnguessableToken& input_stream_id,
+        const std::string& raw_output_device_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
+  forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
+                                                     raw_output_device_id);
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h
index 49ec117..ceafa6a 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h
@@ -5,72 +5,34 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_INPUT_STREAM_FACTORY_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_INPUT_STREAM_FACTORY_H_
 
-#include <cstdint>
-#include <string>
+#include <memory>
 
 #include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "content/browser/renderer_host/media/audio_input_device_manager.h"
 #include "content/common/content_export.h"
 #include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/common/media_stream_request.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace media {
-class AudioParameters;
-}  // namespace media
 
 namespace content {
 
-class AudioInputDeviceManager;
+class MediaStreamManager;
 class RenderFrameHost;
 
-// Handles a RendererAudioInputStreamFactory request for a render frame host,
-// using the provided RendererAudioInputStreamFactoryContext. This class may
-// be constructed on any thread, but must be used on the IO thread after that,
-// and also destructed on the IO thread.
-class CONTENT_EXPORT RenderFrameAudioInputStreamFactory
-    : public mojom::RendererAudioInputStreamFactory {
+// Handles a RendererAudioInputStreamFactory request for a render frame host.
+// Should be constructed and destructed on the UI thread, but will process mojo
+// messages on the IO thread. This class relates to ForwardingAudioStreamFactory
+// the same way as RenderFrameAudioOutputStreamFactory, and a class diagram can
+// be found in render_frame_audio_output_stream_factory.h
+class CONTENT_EXPORT RenderFrameAudioInputStreamFactory final {
  public:
   RenderFrameAudioInputStreamFactory(
       mojom::RendererAudioInputStreamFactoryRequest request,
-      scoped_refptr<AudioInputDeviceManager> audio_input_device_manager,
+      MediaStreamManager* media_stream_manager,
       RenderFrameHost* render_frame_host);
 
-  ~RenderFrameAudioInputStreamFactory() override;
+  ~RenderFrameAudioInputStreamFactory();
 
  private:
-  // mojom::RendererAudioInputStreamFactory implementation.
-  void CreateStream(
-      mojom::RendererAudioInputStreamFactoryClientPtr client,
-      int32_t session_id,
-      const media::AudioParameters& audio_params,
-      bool automatic_gain_control,
-      uint32_t shared_memory_count,
-      audio::mojom::AudioProcessingConfigPtr processing_config) override;
-
-  void CreateStreamAfterLookingUpDevice(
-      mojom::RendererAudioInputStreamFactoryClientPtr client,
-      const media::AudioParameters& audio_params,
-      bool automatic_gain_control,
-      uint32_t shared_memory_count,
-      audio::mojom::AudioProcessingConfigPtr processing_config,
-      const MediaStreamDevice& device);
-
-  void AssociateInputAndOutputForAec(
-      const base::UnguessableToken& input_stream_id,
-      const std::string& output_device_id) override;
-
-  void AssociateTranslatedOutputDeviceForAec(
-      const base::UnguessableToken& input_stream_id,
-      const std::string& raw_output_device_id);
-
-  const mojo::Binding<RendererAudioInputStreamFactory> binding_;
-  const scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_;
-  RenderFrameHost* const render_frame_host_;
-
-  base::WeakPtrFactory<RenderFrameAudioInputStreamFactory> weak_ptr_factory_;
+  class Core;
+  std::unique_ptr<Core> core_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameAudioInputStreamFactory);
 };
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
index 654ec5a..f2cb24b 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h"
 
-#include <memory>
 #include <string>
 #include <utility>
 
@@ -14,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
 #include "content/browser/media/forwarding_audio_stream_factory.h"
+#include "content/browser/renderer_host/media/audio_input_device_manager.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -66,6 +66,7 @@
     // Set up the ForwardingAudioStreamFactory.
     service_manager::Connector::TestApi connector_test_api(
         ForwardingAudioStreamFactory::ForFrame(main_rfh())
+            ->core()
             ->get_connector_for_testing());
     connector_test_api.OverrideBinderForTesting(
         service_manager::Identity(audio::mojom::kServiceName),
@@ -183,17 +184,15 @@
 
 TEST_F(RenderFrameAudioInputStreamFactoryTest, ConstructDestruct) {
   mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
-  RenderFrameAudioInputStreamFactory factory(mojo::MakeRequest(&factory_ptr),
-                                             audio_input_device_manager(),
-                                             main_rfh());
+  RenderFrameAudioInputStreamFactory factory(
+      mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 }
 
 TEST_F(RenderFrameAudioInputStreamFactoryTest,
        CreateOpenedStream_ForwardsCall) {
   mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
-  RenderFrameAudioInputStreamFactory factory(mojo::MakeRequest(&factory_ptr),
-                                             audio_input_device_manager(),
-                                             main_rfh());
+  RenderFrameAudioInputStreamFactory factory(
+      mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 
   int session_id = audio_input_device_manager()->Open(
       MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, kDeviceId, kDeviceName));
@@ -213,9 +212,8 @@
        CreateWebContentsCapture_ForwardsCall) {
   std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
   mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
-  RenderFrameAudioInputStreamFactory factory(mojo::MakeRequest(&factory_ptr),
-                                             audio_input_device_manager(),
-                                             main_rfh());
+  RenderFrameAudioInputStreamFactory factory(
+      mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 
   RenderFrameHost* main_frame = source_contents->GetMainFrame();
   WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
@@ -238,9 +236,8 @@
        CreateWebContentsCaptureAfterCaptureSourceDestructed_Fails) {
   std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
   mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
-  RenderFrameAudioInputStreamFactory factory(mojo::MakeRequest(&factory_ptr),
-                                             audio_input_device_manager(),
-                                             main_rfh());
+  RenderFrameAudioInputStreamFactory factory(
+      mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 
   RenderFrameHost* main_frame = source_contents->GetMainFrame();
   WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
@@ -263,9 +260,8 @@
 TEST_F(RenderFrameAudioInputStreamFactoryTest,
        CreateStreamWithoutValidSessionId_Fails) {
   mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
-  RenderFrameAudioInputStreamFactory factory(mojo::MakeRequest(&factory_ptr),
-                                             audio_input_device_manager(),
-                                             main_rfh());
+  RenderFrameAudioInputStreamFactory factory(
+      mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
 
   int session_id = 123;
   mojom::RendererAudioInputStreamFactoryClientPtr client;
diff --git a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.cc b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.cc
index d9454f8..5d03441b 100644
--- a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.cc
@@ -4,73 +4,141 @@
 
 #include "content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h"
 
+#include <cstdint>
+#include <string>
 #include <utility>
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/containers/flat_set.h"
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/task/post_task.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "base/unguessable_token.h"
 #include "content/browser/media/forwarding_audio_stream_factory.h"
 #include "content/browser/renderer_host/media/audio_output_authorization_handler.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
-#include "media/base/bind_to_current_loop.h"
+#include "media/base/output_device_info.h"
+#include "media/mojo/interfaces/audio_output_stream.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace content {
 
-// This class implements media::mojom::AudioOutputStreamProvider for a single
-// streams and cleans itself up (using the |owner| pointer) when done.
-class RenderFrameAudioOutputStreamFactory::ProviderImpl final
-    : public media::mojom::AudioOutputStreamProvider {
+class RenderFrameAudioOutputStreamFactory::Core final
+    : public mojom::RendererAudioOutputStreamFactory {
  public:
-  ProviderImpl(media::mojom::AudioOutputStreamProviderRequest request,
-               RenderFrameAudioOutputStreamFactory* owner,
-               const std::string& device_id)
-      : owner_(owner),
-        device_id_(device_id),
-        binding_(this, std::move(request)) {
-    DCHECK(owner_);
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    // Unretained is safe since |this| owns |binding_|.
-    binding_.set_connection_error_handler(
-        base::BindOnce(&ProviderImpl::Done, base::Unretained(this)));
+  Core(RenderFrameHost* frame,
+       media::AudioSystem* audio_system,
+       MediaStreamManager* media_stream_manager,
+       mojom::RendererAudioOutputStreamFactoryRequest request);
+
+  ~Core() final = default;
+
+  void Init(mojom::RendererAudioOutputStreamFactoryRequest request);
+
+  size_t current_number_of_providers_for_testing() {
+    return stream_providers_.size();
   }
 
-  ~ProviderImpl() final { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
-
-  void Acquire(
-      const media::AudioParameters& params,
-      media::mojom::AudioOutputStreamProviderClientPtr provider_client,
-      const base::Optional<base::UnguessableToken>& processing_id) final {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    TRACE_EVENT1("audio",
-                 "RenderFrameAudioOutputStreamFactory::ProviderImpl::Acquire",
-                 "raw device id", device_id_);
-
-    RenderFrameHost* frame = owner_->frame_;
-    ForwardingAudioStreamFactory* factory =
-        ForwardingAudioStreamFactory::ForFrame(frame);
-    if (factory) {
-      // It's possible that |frame| has already been destroyed, in which case we
-      // don't need to create a stream. In this case, the renderer will get a
-      // connection error since |provider_client| is dropped.
-      factory->CreateOutputStream(frame, device_id_, params, processing_id,
-                                  std::move(provider_client));
-    }
-
-    // Since the stream creation has been propagated, |this| is no longer
-    // needed.
-    Done();
-  }
-
-  void Done() { owner_->DeleteProvider(this); }
-
  private:
-  RenderFrameAudioOutputStreamFactory* const owner_;
-  const std::string device_id_;
+  // This class implements media::mojom::AudioOutputStreamProvider for a single
+  // streams and cleans itself up (using the |owner| pointer) when done.
+  class ProviderImpl final : public media::mojom::AudioOutputStreamProvider {
+   public:
+    ProviderImpl(media::mojom::AudioOutputStreamProviderRequest request,
+                 RenderFrameAudioOutputStreamFactory::Core* owner,
+                 const std::string& device_id)
+        : owner_(owner),
+          device_id_(device_id),
+          binding_(this, std::move(request)) {
+      DCHECK_CURRENTLY_ON(BrowserThread::IO);
+      // Unretained is safe since |this| owns |binding_|.
+      binding_.set_connection_error_handler(
+          base::BindOnce(&ProviderImpl::Done, base::Unretained(this)));
+    }
 
-  mojo::Binding<media::mojom::AudioOutputStreamProvider> binding_;
+    ~ProviderImpl() final { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
 
-  DISALLOW_COPY_AND_ASSIGN(ProviderImpl);
+    void Acquire(
+        const media::AudioParameters& params,
+        media::mojom::AudioOutputStreamProviderClientPtr provider_client,
+        const base::Optional<base::UnguessableToken>& processing_id) final {
+      DCHECK_CURRENTLY_ON(BrowserThread::IO);
+      TRACE_EVENT1("audio",
+                   "RenderFrameAudioOutputStreamFactory::ProviderImpl::Acquire",
+                   "raw device id", device_id_);
+
+      owner_->forwarding_factory_->CreateOutputStream(
+          owner_->process_id_, owner_->frame_id_, device_id_, params,
+          processing_id, std::move(provider_client));
+
+      // Since the stream creation has been propagated, |this| is no longer
+      // needed.
+      Done();
+    }
+
+    void Done() { owner_->DeleteProvider(this); }
+
+   private:
+    RenderFrameAudioOutputStreamFactory::Core* const owner_;
+    const std::string device_id_;
+
+    mojo::Binding<media::mojom::AudioOutputStreamProvider> binding_;
+
+    DISALLOW_COPY_AND_ASSIGN(ProviderImpl);
+  };
+
+  using OutputStreamProviderSet =
+      base::flat_set<std::unique_ptr<media::mojom::AudioOutputStreamProvider>,
+                     base::UniquePtrComparator>;
+
+  // mojom::RendererAudioOutputStreamFactory implementation.
+  void RequestDeviceAuthorization(
+      media::mojom::AudioOutputStreamProviderRequest provider_request,
+      int32_t session_id,
+      const std::string& device_id,
+      RequestDeviceAuthorizationCallback callback) final;
+
+  // Here, the |raw_device_id| is used to create the stream, and
+  // |device_id_for_renderer| is nonempty in the case when the renderer
+  // requested a device using a |session_id|, to let it know which device was
+  // chosen. This id is hashed.
+  void AuthorizationCompleted(
+      base::TimeTicks auth_start_time,
+      media::mojom::AudioOutputStreamProviderRequest request,
+      RequestDeviceAuthorizationCallback callback,
+      media::OutputDeviceStatus status,
+      const media::AudioParameters& params,
+      const std::string& raw_device_id,
+      const std::string& device_id_for_renderer);
+
+  void DeleteProvider(media::mojom::AudioOutputStreamProvider* stream_provider);
+
+  const int process_id_;
+  const int frame_id_;
+  AudioOutputAuthorizationHandler authorization_handler_;
+
+  mojo::Binding<mojom::RendererAudioOutputStreamFactory> binding_;
+  ForwardingAudioStreamFactory::Core* forwarding_factory_;
+
+  // The OutputStreamProviders for authorized streams are kept here while
+  // waiting for the renderer to finish creating the stream, and destructed
+  // afterwards.
+  OutputStreamProviderSet stream_providers_;
+
+  // Weak pointers are used to cancel device authorizations that are in flight
+  // while |this| is destructed.
+  base::WeakPtrFactory<Core> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
 };
 
 RenderFrameAudioOutputStreamFactory::RenderFrameAudioOutputStreamFactory(
@@ -78,53 +146,90 @@
     media::AudioSystem* audio_system,
     MediaStreamManager* media_stream_manager,
     mojom::RendererAudioOutputStreamFactoryRequest request)
-    : binding_(this, std::move(request)),
-      frame_(frame),
-      authorization_handler_(
-          new AudioOutputAuthorizationHandler(audio_system,
-                                              media_stream_manager,
-                                              frame_->GetProcess()->GetID())),
-      weak_ptr_factory_(this) {
+    : core_(new Core(frame,
+                     audio_system,
+                     media_stream_manager,
+                     std::move(request))) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 RenderFrameAudioOutputStreamFactory::~RenderFrameAudioOutputStreamFactory() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
+  // as it doesn't post in case it is already executed on the right thread. That
+  // causes issues in unit tests where the UI thread and the IO thread are the
+  // same.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
 }
 
-void RenderFrameAudioOutputStreamFactory::RequestDeviceAuthorization(
+size_t
+RenderFrameAudioOutputStreamFactory::CurrentNumberOfProvidersForTesting() {
+  return core_->current_number_of_providers_for_testing();
+}
+
+RenderFrameAudioOutputStreamFactory::Core::Core(
+    RenderFrameHost* frame,
+    media::AudioSystem* audio_system,
+    MediaStreamManager* media_stream_manager,
+    mojom::RendererAudioOutputStreamFactoryRequest request)
+    : process_id_(frame->GetProcess()->GetID()),
+      frame_id_(frame->GetRoutingID()),
+      authorization_handler_(audio_system, media_stream_manager, process_id_),
+      binding_(this),
+      forwarding_factory_(ForwardingAudioStreamFactory::CoreForFrame(frame)),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!forwarding_factory_) {
+    // The only case when we not have a forwarding factory is when the
+    // frame belongs to an interstitial. Interstitials don't need audio, so it's
+    // fine to drop the request.
+    return;
+  }
+
+  // Unretained is safe since the destruction of |this| is posted to the IO
+  // thread.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&Core::Init, base::Unretained(this), std::move(request)));
+}
+
+void RenderFrameAudioOutputStreamFactory::Core::Init(
+    mojom::RendererAudioOutputStreamFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
+
+  binding_.Bind(std::move(request));
+}
+
+void RenderFrameAudioOutputStreamFactory::Core::RequestDeviceAuthorization(
     media::mojom::AudioOutputStreamProviderRequest provider_request,
     int32_t session_id,
     const std::string& device_id,
     RequestDeviceAuthorizationCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(forwarding_factory_);
   TRACE_EVENT2(
       "audio",
       "RenderFrameAudioOutputStreamFactory::RequestDeviceAuthorization",
       "device id", device_id, "session_id", session_id);
 
   const base::TimeTicks auth_start_time = base::TimeTicks::Now();
-  // TODO(https://crbug.com/837625): This thread hopping is suboptimal since
-  // AudioOutputAuthorizationHandler was made to be used on the IO thread.
-  // Make AudioOutputAuthorizationHandler work on the UI thread instead.
-  AudioOutputAuthorizationHandler::AuthorizationCompletedCallback
-      completed_callback = media::BindToCurrentLoop(base::BindOnce(
-          &RenderFrameAudioOutputStreamFactory::AuthorizationCompleted,
-          weak_ptr_factory_.GetWeakPtr(), auth_start_time,
-          std::move(provider_request), std::move(callback)));
 
-  // Unretained is safe since |authorization_handler_| is deleted on the IO
-  // thread.
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(
-          &AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
-          base::Unretained(authorization_handler_.get()),
-          frame_->GetRoutingID(), session_id, device_id,
-          std::move(completed_callback)));
+  AudioOutputAuthorizationHandler::AuthorizationCompletedCallback
+      completed_callback = base::BindOnce(
+          &RenderFrameAudioOutputStreamFactory::Core::AuthorizationCompleted,
+          weak_ptr_factory_.GetWeakPtr(), auth_start_time,
+          std::move(provider_request), std::move(callback));
+
+  authorization_handler_.RequestDeviceAuthorization(
+      frame_id_, session_id, device_id, std::move(completed_callback));
 }
 
-void RenderFrameAudioOutputStreamFactory::AuthorizationCompleted(
+void RenderFrameAudioOutputStreamFactory::Core::AuthorizationCompleted(
     base::TimeTicks auth_start_time,
     media::mojom::AudioOutputStreamProviderRequest request,
     RequestDeviceAuthorizationCallback callback,
@@ -132,7 +237,7 @@
     const media::AudioParameters& params,
     const std::string& raw_device_id,
     const std::string& device_id_for_renderer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   TRACE_EVENT2("audio",
                "RenderFrameAudioOutputStreamFactory::AuthorizationCompleted",
                "raw device id", raw_device_id, "status", status);
@@ -150,9 +255,9 @@
   }
 }
 
-void RenderFrameAudioOutputStreamFactory::DeleteProvider(
+void RenderFrameAudioOutputStreamFactory::Core::DeleteProvider(
     media::mojom::AudioOutputStreamProvider* stream_provider) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   size_t deleted = stream_providers_.erase(stream_provider);
   DCHECK_EQ(1u, deleted);
 }
diff --git a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h
index fbc229b..92c5eb29 100644
--- a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h
+++ b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h
@@ -5,36 +5,46 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_OUTPUT_STREAM_FACTORY_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_OUTPUT_STREAM_FACTORY_H_
 
+#include <cstddef>
 #include <memory>
-#include <string>
 
-#include "base/containers/flat_set.h"
-#include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "content/common/media/renderer_audio_output_stream_factory.mojom.h"
-#include "content/public/browser/browser_thread.h"
-#include "media/base/output_device_info.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
 namespace media {
 class AudioSystem;
-class AudioParameters;
 }  // namespace media
 
 namespace content {
 
-class AudioOutputAuthorizationHandler;
 class MediaStreamManager;
 class RenderFrameHost;
 
-// This class, which lives on the UI thread, takes care of stream requests from
-// a render frame. It verifies that the stream creation is allowed and then
-// forwards the request to the appropriate ForwardingAudioStreamFactory.
-class CONTENT_EXPORT RenderFrameAudioOutputStreamFactory
-    : public mojom::RendererAudioOutputStreamFactory {
+// This class is related to ForwardingAudioStreamFactory as follows:
+//
+//     WebContentsImpl       <--        RenderFrameHostImpl
+//           ^                                  ^
+//           |                                  |
+//  ForwardingAudioStreamFactory   RenderFrameAudioOutputStreamFactory
+//           ^                                  ^
+//           |                                  |
+//      FASF::Core           <--          RFAOSF::Core
+//
+// Both FASF::Core and RFAOSF::Core live on (and are destructed on) the IO
+// thread. ForwardingAudioStreamFactory exists until destruction of its owning
+// WebContentsImpl. Since WebContentsImpl outlives all of its
+// RenderFrameHostImpls, ForwardingAudioStreamFactory will outlive
+// RenderFrameAudioOutputStreamFactory. As a consequence, the destruction of
+// FASF::Core will be posted to the IO thread after the destruction of
+// RFAOSF::Core has been posted, so RFAOSF::Core can safely have a raw pointer
+// to FASF::Core
+
+// This class takes care of stream requests from a render frame. It verifies
+// that the stream creation is allowed and then forwards the request to the
+// appropriate ForwardingAudioStreamFactory. It should be constructed and
+// destructed on the UI thread, but will process mojo messages on the IO thread.
+class CONTENT_EXPORT RenderFrameAudioOutputStreamFactory final {
  public:
   RenderFrameAudioOutputStreamFactory(
       RenderFrameHost* frame,
@@ -42,56 +52,13 @@
       MediaStreamManager* media_stream_manager,
       mojom::RendererAudioOutputStreamFactoryRequest request);
 
-  ~RenderFrameAudioOutputStreamFactory() override;
+  ~RenderFrameAudioOutputStreamFactory();
 
-  size_t current_number_of_providers_for_testing() {
-    return stream_providers_.size();
-  }
+  size_t CurrentNumberOfProvidersForTesting();
 
  private:
-  class ProviderImpl;
-  friend class ProviderImpl;  // For DeleteProvider.
-
-  using OutputStreamProviderSet =
-      base::flat_set<std::unique_ptr<media::mojom::AudioOutputStreamProvider>,
-                     base::UniquePtrComparator>;
-
-  // mojom::RendererAudioOutputStreamFactory implementation.
-  void RequestDeviceAuthorization(
-      media::mojom::AudioOutputStreamProviderRequest provider_request,
-      int32_t session_id,
-      const std::string& device_id,
-      RequestDeviceAuthorizationCallback callback) override;
-
-  // Here, the |raw_device_id| is used to create the stream, and
-  // |device_id_for_renderer| is nonempty in the case when the renderer
-  // requested a device using a |session_id|, to let it know which device was
-  // chosen. This id is hashed.
-  void AuthorizationCompleted(
-      base::TimeTicks auth_start_time,
-      media::mojom::AudioOutputStreamProviderRequest request,
-      RequestDeviceAuthorizationCallback callback,
-      media::OutputDeviceStatus status,
-      const media::AudioParameters& params,
-      const std::string& raw_device_id,
-      const std::string& device_id_for_renderer);
-
-  void DeleteProvider(media::mojom::AudioOutputStreamProvider* stream_provider);
-
-  const mojo::Binding<mojom::RendererAudioOutputStreamFactory> binding_;
-  RenderFrameHost* const frame_;
-  const std::unique_ptr<AudioOutputAuthorizationHandler,
-                        BrowserThread::DeleteOnIOThread>
-      authorization_handler_;
-
-  // The OutputStreamProviders for authorized streams are kept here while
-  // waiting for the renderer to finish creating the stream, and destructed
-  // afterwards.
-  OutputStreamProviderSet stream_providers_;
-
-  // Weak pointers are used to cancel device authorizations that are in flight
-  // while |this| is destructed.
-  base::WeakPtrFactory<RenderFrameAudioOutputStreamFactory> weak_ptr_factory_;
+  class Core;
+  std::unique_ptr<Core> core_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameAudioOutputStreamFactory);
 };
diff --git a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
index b8e8850..f7f4fc6 100644
--- a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -69,6 +70,7 @@
     // Set up the ForwardingAudioStreamFactory.
     service_manager::Connector::TestApi connector_test_api(
         ForwardingAudioStreamFactory::ForFrame(main_rfh())
+            ->core()
             ->get_connector_for_testing());
     connector_test_api.OverrideBinderForTesting(
         service_manager::Identity(audio::mojom::kServiceName),
@@ -155,7 +157,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1u, factory.current_number_of_providers_for_testing());
+  EXPECT_EQ(1u, factory.CurrentNumberOfProvidersForTesting());
 }
 
 TEST_F(
@@ -178,7 +180,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0u, factory.current_number_of_providers_for_testing());
+  EXPECT_EQ(0u, factory.CurrentNumberOfProvidersForTesting());
 }
 
 TEST_F(
@@ -200,7 +202,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0u, factory.current_number_of_providers_for_testing());
+  EXPECT_EQ(0u, factory.CurrentNumberOfProvidersForTesting());
 }
 
 TEST_F(RenderFrameAudioOutputStreamFactoryTest,
@@ -228,7 +230,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(!!audio_service_stream_factory_.last_created_callback);
-  EXPECT_EQ(0u, factory.current_number_of_providers_for_testing());
+  EXPECT_EQ(0u, factory.CurrentNumberOfProvidersForTesting());
 }
 
 TEST_F(RenderFrameAudioOutputStreamFactoryTest,
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index 091fa3c..d0942e10 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
 #include "content/test/fake_leveldb_database.h"
 #include "net/base/features.h"
 #include "net/base/test_completion_callback.h"
@@ -306,6 +307,9 @@
     std::vector<uint8_t> data_vector(data.begin(), data.end());
     GetCache(cache)->WriteData(url, origin, base::Time::Now(), data_vector);
     base::RunLoop().RunUntilIdle();
+    // TODO(crbug.com/886892): Remove this once we update GeneratedCodeCache
+    // to serialize operations corresponding to each entry.
+    content::RunAllTasksUntilIdle();
   }
 
   std::string received_data() { return received_data_; }
@@ -1335,14 +1339,15 @@
 }
 
 TEST_F(StoragePartitionImplTest, ClearCodeCache) {
-  // Run this test only when the IsolatedCodeCache feature is enabled
-  if (!base::FeatureList::IsEnabled(net::features::kIsolatedCodeCache))
-    return;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(net::features::kIsolatedCodeCache);
+  ASSERT_TRUE(base::FeatureList::IsEnabled(net::features::kIsolatedCodeCache));
 
   StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
       BrowserContext::GetDefaultStoragePartition(browser_context()));
   // Ensure code cache is initialized.
   base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(partition->GetGeneratedCodeCacheContext() != nullptr);
 
   RemoveCodeCacheTester tester(partition->GetGeneratedCodeCacheContext());
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java b/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java
index 7b1fc0c..400f18c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java
@@ -150,7 +150,7 @@
             // Set this on the content view instead of on the PopupWindow so we can retrieve the
             // padding later.
             mContentView.setBackground(ApiCompatibilityUtils.getDrawable(
-                    mContext.getResources(), R.drawable.dropdown_popup_background));
+                    mContext.getResources(), R.drawable.popup_bg));
         }
 
         // mPopupVerticalMargin is the minimum amount of space we want to have between the popup
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 2301015..9620a913 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -656,11 +656,6 @@
 // Enable IOSurface based screen capturer.
 const base::Feature kIOSurfaceCapturer{"IOSurfaceCapturer",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
-
-// The V2 sandbox on MacOS removes the unsandboed warmup phase and sandboxes the
-// entire life of the process.
-const base::Feature kMacV2Sandbox{"MacV2Sandbox",
-                                  base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // defined(OS_MACOSX)
 
 enum class VideoCaptureServiceConfiguration {
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 92b0561..9305262 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -159,7 +159,6 @@
 #if defined(OS_MACOSX)
 CONTENT_EXPORT extern const base::Feature kDeviceMonitorMac;
 CONTENT_EXPORT extern const base::Feature kIOSurfaceCapturer;
-CONTENT_EXPORT extern const base::Feature kMacV2Sandbox;
 #endif  // defined(OS_MACOSX)
 
 // DON'T ADD RANDOM STUFF HERE. Put it in the main section above in
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index a18e1a92..668f54b 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -236,6 +236,11 @@
   return true;
 }
 
+base::Optional<std::string>
+ContentRendererClient::WebRTCPlatformSpecificAudioProcessingConfiguration() {
+  return base::Optional<std::string>();
+}
+
 GURL ContentRendererClient::OverrideFlashEmbedWithHTML(const GURL& url) {
   return GURL();
 }
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 794ef35..3166cbe2 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -15,6 +15,7 @@
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/task/task_scheduler/task_scheduler.h"
 #include "build/build_config.h"
@@ -378,6 +379,14 @@
   // routing logic, i.e. allowing multiple routes and non-proxied UDP.
   virtual bool ShouldEnforceWebRTCRoutingPreferences();
 
+  // Provides a default configuration of WebRTC audio processing, in JSON format
+  // with fields corresponding to webrtc::AudioProcessing::Config. Allows for a
+  // more functional tuning on platforms with known implementation and hardware
+  // limitations.
+  // This is currently not supported when running the Chrome audio service.
+  virtual base::Optional<std::string>
+  WebRTCPlatformSpecificAudioProcessingConfiguration();
+
   // Notifies that a worker context has been created. This function is called
   // from the worker thread.
   virtual void DidInitializeWorkerContextOnWorkerThread(
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 5c6fae65..883df3b1 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -711,6 +711,8 @@
     "//third_party/webrtc/api:libjingle_logging_api",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
     "//third_party/webrtc/api:rtc_stats_api",
+    "//third_party/webrtc/api/audio:aec3_config",
+    "//third_party/webrtc/api/audio:aec3_config_json",
     "//third_party/webrtc/api/audio:aec3_factory",
     "//third_party/webrtc/api/audio_codecs:audio_codecs_api",
     "//third_party/webrtc/api/audio_codecs/L16:audio_decoder_L16",
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 4fc82f3..dd7f440d 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -515,6 +515,7 @@
   }
 
   network::URLLoaderCompletionStatus renderer_status(status);
+  // TODO(toyoshim): Consider to convert status.cors_preflight_timing_info here.
   if (status.completion_time.is_null()) {
     // No completion timestamp is provided, leave it as is.
   } else if (request_info->remote_request_start.is_null() ||
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 797316a..3479b62 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -978,7 +978,8 @@
     } else {
       client_->DidFinishLoading(status.completion_time, total_transfer_size,
                                 encoded_body_size, status.decoded_body_length,
-                                status.should_report_corb_blocking);
+                                status.should_report_corb_blocking,
+                                status.cors_preflight_timing_info);
     }
   }
 }
diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc
index 1a1c1286..d9a336e 100644
--- a/content/renderer/loader/web_url_loader_impl_unittest.cc
+++ b/content/renderer/loader/web_url_loader_impl_unittest.cc
@@ -240,11 +240,13 @@
       loader_.reset();
   }
 
-  void DidFinishLoading(base::TimeTicks finishTime,
-                        int64_t totalEncodedDataLength,
-                        int64_t totalEncodedBodyLength,
-                        int64_t totalDecodedBodyLength,
-                        bool should_report_corb_blocking) override {
+  void DidFinishLoading(
+      base::TimeTicks finishTime,
+      int64_t totalEncodedDataLength,
+      int64_t totalEncodedBodyLength,
+      int64_t totalDecodedBodyLength,
+      bool should_report_corb_blocking,
+      const std::vector<network::cors::PreflightTimingInfo>&) override {
     EXPECT_TRUE(loader_);
     EXPECT_TRUE(did_receive_response_);
     EXPECT_FALSE(did_finish_);
diff --git a/content/renderer/media/stream/media_stream_audio_processor.cc b/content/renderer/media/stream/media_stream_audio_processor.cc
index b3989eb..444d282 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor.cc
@@ -22,7 +22,9 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
+#include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/media/webrtc/webrtc_audio_device_impl.h"
 #include "media/base/audio_converter.h"
 #include "media/base/audio_fifo.h"
@@ -30,6 +32,8 @@
 #include "media/base/channel_layout.h"
 #include "media/webrtc/echo_information.h"
 #include "media/webrtc/webrtc_switches.h"
+#include "third_party/webrtc/api/audio/echo_canceller3_config.h"
+#include "third_party/webrtc/api/audio/echo_canceller3_config_json.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing_statistics.h"
@@ -598,11 +602,22 @@
   if (properties.echo_cancellation_type ==
       EchoCancellationType::kEchoCancellationAec3) {
     webrtc::EchoCanceller3Config aec3_config;
-    aec3_config.ep_strength.bounded_erl =
+    base::Optional<std::string> audio_processing_platform_config_json =
+        GetContentClient()
+            ->renderer()
+            ->WebRTCPlatformSpecificAudioProcessingConfiguration();
+    if (audio_processing_platform_config_json) {
+      aec3_config = webrtc::Aec3ConfigFromJsonString(
+          *audio_processing_platform_config_json);
+      bool config_parameters_already_valid =
+          webrtc::EchoCanceller3Config::Validate(&aec3_config);
+      RTC_DCHECK(config_parameters_already_valid);
+    }
+    aec3_config.ep_strength.bounded_erl |=
         base::FeatureList::IsEnabled(features::kWebRtcAecBoundedErlSetup);
-    aec3_config.echo_removal_control.has_clock_drift =
+    aec3_config.echo_removal_control.has_clock_drift |=
         base::FeatureList::IsEnabled(features::kWebRtcAecClockDriftSetup);
-    aec3_config.echo_audibility.use_stationary_properties =
+    aec3_config.echo_audibility.use_stationary_properties |=
         base::FeatureList::IsEnabled(features::kWebRtcAecNoiseTransparency);
 
     ap_builder.SetEchoControlFactory(
diff --git a/content/test/data/font_src_local_matching.html b/content/test/data/font_src_local_matching.html
index 163d4f46..e12fc4c 100644
--- a/content/test/data/font_src_local_matching.html
+++ b/content/test/data/font_src_local_matching.html
@@ -84,12 +84,25 @@
       ["muktinarrow", "0"],
       ["Tinos-Regular", "0"]
     ];
-  }
+  } else if (navigator.userAgent.indexOf("Macintosh") !== -1) {
+    return [
+      [ "AmericanTypewriter-CondensedLight", "0" ],
+      [ "ArialNarrow-BoldItalic", "0" ],
+      [ "Baskerville-SemiBoldItalic", "0" ],
+      [ "DevanagariMT", "0" ],
+      [ "DINAlternate-Bold", "0" ],
+      [ "GillSans-LightItalic", "0" ],
+      [ "IowanOldStyle-Titling", "0" ],
+      [ "MalayalamSangamMN", "0" ],
+      [ "HiraMaruPro-W4", "0" ],
+      [ "HiraKakuStdN-W8", "0" ],
+    ]
+  };
   return [];
 }
 
 function stripSpaces(fontName) {
-    return fontName.replace(/\s+/g, '');
+  return fontName.replace(/\s+/g, '');
 }
 
 function addTestNodes() {
diff --git a/gpu/command_buffer/service/gl_context_virtual.cc b/gpu/command_buffer/service/gl_context_virtual.cc
index 7d83352..3d19ed8e 100644
--- a/gpu/command_buffer/service/gl_context_virtual.cc
+++ b/gpu/command_buffer/service/gl_context_virtual.cc
@@ -121,6 +121,10 @@
 void GLContextVirtual::BackpressureFenceWait(uint64_t fence) {
   shared_context_->BackpressureFenceWait(fence);
 }
+
+void GLContextVirtual::FlushForDriverCrashWorkaround() {
+  shared_context_->FlushForDriverCrashWorkaround();
+}
 #endif
 
 GLContextVirtual::~GLContextVirtual() {
diff --git a/gpu/command_buffer/service/gl_context_virtual.h b/gpu/command_buffer/service/gl_context_virtual.h
index f0156ead..5ed61a9 100644
--- a/gpu/command_buffer/service/gl_context_virtual.h
+++ b/gpu/command_buffer/service/gl_context_virtual.h
@@ -50,6 +50,7 @@
 #if defined(OS_MACOSX)
   uint64_t BackpressureFenceCreate() override;
   void BackpressureFenceWait(uint64_t fence) override;
+  void FlushForDriverCrashWorkaround() override;
 #endif
 
  protected:
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 151500b..08706db0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -5692,6 +5692,15 @@
     }
   }
 
+#if defined(OS_MACOSX)
+  // Aggressively call glFlush on macOS. This is the only fix that has been
+  // found so far to avoid crashes on Intel drivers. The workaround
+  // isn't needed for WebGL contexts, though.
+  // https://crbug.com/863817
+  if (!feature_info_->IsWebGLContext())
+    context_->FlushForDriverCrashWorkaround();
+#endif
+
   *entries_processed = process_pos;
 
   if (error::IsError(result)) {
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index d4fe5e5d..de2ae81 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -61,8 +61,10 @@
     "//ios/chrome/common/app_group",
     "//ios/chrome/test:test_support",
     "//ios/chrome/test/base",
-    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser:browser",
+    "//ios/public/provider/chrome/browser:test_support",
     "//ios/public/provider/chrome/browser/distribution",
+    "//ios/public/provider/chrome/browser/distribution:test_support",
     "//ios/web/public/test:test",
     "//testing/gtest",
     "//third_party/ocmock",
diff --git a/ios/chrome/app/firebase_utils.h b/ios/chrome/app/firebase_utils.h
index cdbef3cb..cce0e54 100644
--- a/ios/chrome/app/firebase_utils.h
+++ b/ios/chrome/app/firebase_utils.h
@@ -27,8 +27,12 @@
   // Firebase Analytics is for installation reporting only. Once a user has
   // passed the conversion attribution window, there is nothing to report.
   kDisabledConversionWindow,
+  // Users who installed Chrome prior to incorporating Firebase into Chrome
+  // should not initialize Firebase because these users could not have been
+  // the result of any promption campaigns that use Firebase Analytics.
+  kDisabledLegacyInstallation,
   // Count of enum values. Must be equal to the last value above.
-  kMaxValue = kDisabledConversionWindow,
+  kMaxValue = kDisabledLegacyInstallation,
 };
 
 // Initializes Firebase SDK if configured and necessary.
diff --git a/ios/chrome/app/firebase_utils.mm b/ios/chrome/app/firebase_utils.mm
index f9d04a8..72c98ba 100644
--- a/ios/chrome/app/firebase_utils.mm
+++ b/ios/chrome/app/firebase_utils.mm
@@ -13,6 +13,8 @@
 #include "components/prefs/pref_service.h"
 #import "ios/chrome/app/firebase_buildflags.h"
 #include "ios/chrome/browser/application_context.h"
+#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
 #if BUILDFLAG(FIREBASE_ENABLED)
 #import "ios/third_party/firebase/Analytics/FirebaseCore.framework/Headers/FIRApp.h"
 #endif  // BUILDFLAG(FIREBASE_ENABLED)
@@ -32,10 +34,25 @@
   const int64_t install_date = prefs->GetInt64(metrics::prefs::kInstallDate);
   base::TimeDelta installed_delta =
       base::TimeDelta::FromSeconds(base::Time::Now().ToTimeT() - install_date);
-  // Initialize Firebase SDK to allow first_open event to be uploaded until
-  // Attribution Window has passed.
+  // Initialize Firebase SDK only if there is a possibility that user
+  // installed Chrome as a result of some marketing campaigns.
+  // 1. If user installed Chrome prior to the first version of Chrome that
+  //    supports Firebase SDK, this is a legacy user who would not be
+  //    attributable to any marketing event. Firebase SDK should not be
+  //    initialized in this case.
+  // 2. Installation Attribution is the association of a Chrome installation
+  //    to some marketing event. This attribution is valid only if it happens
+  //    within a reasonable timeframe. If installation date is older than
+  //    the acceptable Attribution Window, there is no need to initialize
+  //    Firebase SDK since the first_open event would not be considered for
+  //    attribution to this installation to a marketing event.
   FirebaseConfiguredState enabled_state;
-  if (installed_delta.InDaysFloored() >= kConversionAttributionWindowInDays) {
+  auto* provider =
+      ios::GetChromeBrowserProvider()->GetAppDistributionProvider();
+  if (provider->IsPreFirebaseLegacyUser(install_date)) {
+    enabled_state = FirebaseConfiguredState::kDisabledLegacyInstallation;
+  } else if (installed_delta.InDaysFloored() >=
+             kConversionAttributionWindowInDays) {
     enabled_state = FirebaseConfiguredState::kDisabledConversionWindow;
   } else {
     [FIRApp configure];
diff --git a/ios/chrome/app/firebase_utils_unittest.mm b/ios/chrome/app/firebase_utils_unittest.mm
index fb21636..3f17037f 100644
--- a/ios/chrome/app/firebase_utils_unittest.mm
+++ b/ios/chrome/app/firebase_utils_unittest.mm
@@ -18,6 +18,9 @@
 #if BUILDFLAG(FIREBASE_ENABLED)
 #import "ios/third_party/firebase/Analytics/FirebaseCore.framework/Headers/FIRApp.h"
 #endif  // BUILDFLAG(FIREBASE_ENABLED)
+#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_provider.h"
+#include "ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h"
+#include "ios/public/provider/chrome/browser/test_chrome_browser_provider.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -28,6 +31,29 @@
 #error "This file requires ARC support."
 #endif
 
+#if BUILDFLAG(FIREBASE_ENABLED)
+class LegacyUserAppDistributionProvider : public TestAppDistributionProvider {
+ public:
+  bool IsPreFirebaseLegacyUser(int64_t install_date) override { return true; }
+};
+
+class FakeChromeBrowserProvider : public ios::TestChromeBrowserProvider {
+ public:
+  FakeChromeBrowserProvider()
+      : app_distribution_provider_(
+            std::make_unique<LegacyUserAppDistributionProvider>()) {}
+  ~FakeChromeBrowserProvider() override {}
+
+  AppDistributionProvider* GetAppDistributionProvider() const override {
+    return app_distribution_provider_.get();
+  }
+
+ private:
+  std::unique_ptr<LegacyUserAppDistributionProvider> app_distribution_provider_;
+  DISALLOW_COPY_AND_ASSIGN(FakeChromeBrowserProvider);
+};
+#endif  // BUILDFLAG(FIREBASE_ENABLED)
+
 class FirebaseUtilsTest : public PlatformTest {
  public:
   FirebaseUtilsTest()
@@ -84,7 +110,7 @@
   // window.
   int64_t before_attribution =
       base::TimeDelta::FromDays(kConversionAttributionWindowInDays + 1)
-          .InMilliseconds();
+          .InSeconds();
   SetInstallDate(base::Time::Now().ToTimeT() - before_attribution);
   // Expects Firebase SDK initialization to be not called.
   [[firapp_ reject] configure];
@@ -95,6 +121,23 @@
   EXPECT_OCMOCK_VERIFY(firapp_);
 }
 
+TEST_F(FirebaseUtilsTest, DisabledLegacyInstallation) {
+  // Sets up the ChromeProviderEnvironment with a fake provider that responds
+  // positively that user is a legacy user predating Firebase integration.
+  IOSChromeScopedTestingChromeBrowserProvider provider_(
+      std::make_unique<FakeChromeBrowserProvider>());
+  // Set an app install date that is at least a year ago.
+  int64_t a_year_ago = base::TimeDelta::FromDays(365).InSeconds();
+  SetInstallDate(base::Time::Now().ToTimeT() - a_year_ago);
+  // Expects Firebase SDK initialization to be not called.
+  [[firapp_ reject] configure];
+  InitializeFirebase(/*is_first_run=*/true);
+  histogram_tester_.ExpectUniqueSample(
+      kFirebaseConfiguredHistogramName,
+      FirebaseConfiguredState::kDisabledLegacyInstallation, 1);
+  EXPECT_OCMOCK_VERIFY(firapp_);
+}
+
 #else
 
 TEST_F(FirebaseUtilsTest, DisabledInitializedHistogram) {
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index aff3b33..b6f5a5c 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -87,6 +87,7 @@
     "//components/flags_ui",
     "//components/flags_ui:switches",
     "//components/handoff",
+    "//components/invalidation/impl:impl",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
     "//components/metrics",
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index d75e597..0853824 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -30,6 +30,7 @@
 #include "components/flags_ui/feature_entry_macros.h"
 #include "components/flags_ui/flags_storage.h"
 #include "components/flags_ui/flags_ui_switches.h"
+#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/ntp_tiles/switches.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/password_manager/core/common/password_manager_features.h"
@@ -381,6 +382,9 @@
      flag_descriptions::kOmniboxTabSwitchSuggestionsDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxTabSwitchSuggestions)},
+    {"fcm-invalidations", flag_descriptions::kFCMInvalidationsName,
+     flag_descriptions::kFCMInvalidationsDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(invalidation::switches::kFCMInvalidations)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
index 02df437..38901f1 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
@@ -116,6 +116,7 @@
     auto navigation_manager = std::make_unique<FakeNavigationManager>();
     navigation_manager_ = navigation_manager.get();
     web_state_.SetNavigationManager(std::move(navigation_manager));
+    web_state_.SetCurrentURL(GURL("https://chromium.org"));
     tab_helper_ = AppLauncherTabHelper::FromWebState(&web_state_);
   }
 
diff --git a/ios/chrome/browser/autocomplete/BUILD.gn b/ios/chrome/browser/autocomplete/BUILD.gn
index 27f4696..fd0a873a 100644
--- a/ios/chrome/browser/autocomplete/BUILD.gn
+++ b/ios/chrome/browser/autocomplete/BUILD.gn
@@ -6,8 +6,8 @@
   sources = [
     "autocomplete_classifier_factory.cc",
     "autocomplete_classifier_factory.h",
-    "autocomplete_provider_client_impl.cc",
     "autocomplete_provider_client_impl.h",
+    "autocomplete_provider_client_impl.mm",
     "autocomplete_scheme_classifier_impl.h",
     "autocomplete_scheme_classifier_impl.mm",
     "in_memory_url_index_factory.cc",
@@ -36,7 +36,9 @@
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/unified_consent",
+    "//ios/chrome/browser/web_state_list",
     "//ios/public/provider/chrome/browser",
     "//ios/web",
     "//url",
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.cc b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
similarity index 91%
rename from ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.cc
rename to ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
index 37daccf..5b92bfe 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.cc
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
@@ -28,9 +28,17 @@
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/tabs/tab_model_list.h"
 #include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 AutocompleteProviderClientImpl::AutocompleteProviderClientImpl(
     ios::ChromeBrowserState* browser_state)
     : browser_state_(browser_state),
@@ -217,5 +225,18 @@
 bool AutocompleteProviderClientImpl::IsTabOpenWithURL(
     const GURL& url,
     const AutocompleteInput* input) {
+  TabModel* tab_model =
+      TabModelList::GetLastActiveTabModelForChromeBrowserState(browser_state_);
+  WebStateList* web_state_list = tab_model.webStateList;
+  if (!web_state_list)
+    return false;
+
+  for (int index = 0; index < web_state_list->count(); index++) {
+    web::WebState* web_state = web_state_list->GetWebStateAt(index);
+
+    if (web_state != web_state_list->GetActiveWebState() &&
+        url == web_state->GetVisibleURL())
+      return true;
+  }
   return false;
 }
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 623cefa8..c5f6200 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -27,7 +27,7 @@
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
 #include "ios/chrome/browser/history/web_history_service_factory.h"
-#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/language/language_model_manager_factory.h"
 #include "ios/chrome/browser/language/url_language_histogram_factory.h"
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
@@ -116,7 +116,7 @@
   IOSChromeFaviconLoaderFactory::GetInstance();
   IOSChromeContentSuggestionsServiceFactory::GetInstance();
   IOSChromePasswordStoreFactory::GetInstance();
-  IOSChromeProfileInvalidationProviderFactory::GetInstance();
+  IOSChromeDeprecatedProfileInvalidationProviderFactory::GetInstance();
   ModelTypeStoreServiceFactory::GetInstance();
   ProfileSyncServiceFactory::GetInstance();
   IOSUserEventServiceFactory::GetInstance();
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
index 50c644f..9bc94a70 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
@@ -32,7 +32,7 @@
 #include "ios/chrome/browser/chrome_constants.h"
 #include "ios/chrome/browser/chrome_paths.h"
 #include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h"
-#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/signin/account_fetcher_service_factory.h"
@@ -216,7 +216,7 @@
       ->InitCookieListener();
   ios::AccountConsistencyServiceFactory::GetForBrowserState(browser_state);
   invalidation::ProfileInvalidationProvider* invalidation_provider =
-      IOSChromeProfileInvalidationProviderFactory::GetForBrowserState(
+      IOSChromeDeprecatedProfileInvalidationProviderFactory::GetForBrowserState(
           browser_state);
   invalidation::InvalidationService* invalidation_service =
       invalidation_provider ? invalidation_provider->GetInvalidationService()
diff --git a/ios/chrome/browser/invalidation/BUILD.gn b/ios/chrome/browser/invalidation/BUILD.gn
index 6099a5b..0ac4f06 100644
--- a/ios/chrome/browser/invalidation/BUILD.gn
+++ b/ios/chrome/browser/invalidation/BUILD.gn
@@ -5,8 +5,8 @@
 source_set("invalidation") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "ios_chrome_profile_invalidation_provider_factory.h",
-    "ios_chrome_profile_invalidation_provider_factory.mm",
+    "ios_chrome_deprecated_profile_invalidation_provider_factory.h",
+    "ios_chrome_deprecated_profile_invalidation_provider_factory.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h b/ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h
similarity index 63%
rename from ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h
rename to ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h
index 8c2aa6a..c46f6bc 100644
--- a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h
+++ b/ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
-#define IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
+#ifndef IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_DEPRECATED_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
+#define IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_DEPRECATED_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
 
 #include <memory>
 
@@ -29,7 +29,7 @@
 
 // A BrowserContextKeyedServiceFactory to construct InvalidationServices wrapped
 // in ProfileInvalidationProviders.
-class IOSChromeProfileInvalidationProviderFactory
+class IOSChromeDeprecatedProfileInvalidationProviderFactory
     : public BrowserStateKeyedServiceFactory {
  public:
   // Returns the ProfileInvalidationProvider for the given |browser_state|,
@@ -37,14 +37,14 @@
   static invalidation::ProfileInvalidationProvider* GetForBrowserState(
       ios::ChromeBrowserState* browser_state);
 
-  static IOSChromeProfileInvalidationProviderFactory* GetInstance();
+  static IOSChromeDeprecatedProfileInvalidationProviderFactory* GetInstance();
 
  private:
   friend struct base::DefaultSingletonTraits<
-      IOSChromeProfileInvalidationProviderFactory>;
+      IOSChromeDeprecatedProfileInvalidationProviderFactory>;
 
-  IOSChromeProfileInvalidationProviderFactory();
-  ~IOSChromeProfileInvalidationProviderFactory() override;
+  IOSChromeDeprecatedProfileInvalidationProviderFactory();
+  ~IOSChromeDeprecatedProfileInvalidationProviderFactory() override;
 
   // BrowserStateKeyedServiceFactory:
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
@@ -52,7 +52,8 @@
   void RegisterBrowserStatePrefs(
       user_prefs::PrefRegistrySyncable* registry) override;
 
-  DISALLOW_COPY_AND_ASSIGN(IOSChromeProfileInvalidationProviderFactory);
+  DISALLOW_COPY_AND_ASSIGN(
+      IOSChromeDeprecatedProfileInvalidationProviderFactory);
 };
 
-#endif  // IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
+#endif  // IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_DEPRECATED_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
diff --git a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm b/ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.mm
similarity index 79%
rename from ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm
rename to ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.mm
index 842ef975..51749a5e 100644
--- a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm
+++ b/ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.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.
 
-#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 
 #include <memory>
 #include <utility>
@@ -37,20 +37,21 @@
 
 // static
 invalidation::ProfileInvalidationProvider*
-IOSChromeProfileInvalidationProviderFactory::GetForBrowserState(
+IOSChromeDeprecatedProfileInvalidationProviderFactory::GetForBrowserState(
     ios::ChromeBrowserState* browser_state) {
   return static_cast<ProfileInvalidationProvider*>(
       GetInstance()->GetServiceForBrowserState(browser_state, true));
 }
 
 // static
-IOSChromeProfileInvalidationProviderFactory*
-IOSChromeProfileInvalidationProviderFactory::GetInstance() {
-  return base::Singleton<IOSChromeProfileInvalidationProviderFactory>::get();
+IOSChromeDeprecatedProfileInvalidationProviderFactory*
+IOSChromeDeprecatedProfileInvalidationProviderFactory::GetInstance() {
+  return base::Singleton<
+      IOSChromeDeprecatedProfileInvalidationProviderFactory>::get();
 }
 
-IOSChromeProfileInvalidationProviderFactory::
-    IOSChromeProfileInvalidationProviderFactory()
+IOSChromeDeprecatedProfileInvalidationProviderFactory::
+    IOSChromeDeprecatedProfileInvalidationProviderFactory()
     : BrowserStateKeyedServiceFactory(
           "InvalidationService",
           BrowserStateDependencyManager::GetInstance()) {
@@ -58,11 +59,11 @@
   DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
 }
 
-IOSChromeProfileInvalidationProviderFactory::
-    ~IOSChromeProfileInvalidationProviderFactory() {}
+IOSChromeDeprecatedProfileInvalidationProviderFactory::
+    ~IOSChromeDeprecatedProfileInvalidationProviderFactory() {}
 
 std::unique_ptr<KeyedService>
-IOSChromeProfileInvalidationProviderFactory::BuildServiceInstanceFor(
+IOSChromeDeprecatedProfileInvalidationProviderFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
@@ -88,8 +89,8 @@
       std::move(service), std::move(identity_provider));
 }
 
-void IOSChromeProfileInvalidationProviderFactory::RegisterBrowserStatePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
+void IOSChromeDeprecatedProfileInvalidationProviderFactory::
+    RegisterBrowserStatePrefs(user_prefs::PrefRegistrySyncable* registry) {
   ProfileInvalidationProvider::RegisterProfilePrefs(registry);
   InvalidatorStorage::RegisterProfilePrefs(registry);
 }
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 8723dbf0..0611e54 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -163,6 +163,11 @@
 const char kExternalSearchName[] = "External Search";
 const char kExternalSearchDescription[] = "Enable support for External Search.";
 
+const char kFCMInvalidationsName[] =
+    "Enable invalidations delivery via new FCM based protocol";
+const char kFCMInvalidationsDescription[] =
+    "Use the new FCM-based protocol for deliveling invalidations";
+
 const char kFullscreenViewportAdjustmentExperimentName[] =
     "Fullscreen Viewport Adjustment Mode";
 const char kFullscreenViewportAdjustmentExperimentDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 2e40e59..3c646f12b 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -135,6 +135,10 @@
 extern const char kExternalSearchName[];
 extern const char kExternalSearchDescription[];
 
+// Title and description for the flag to enable invaliations delivery via FCM.
+extern const char kFCMInvalidationsName[];
+extern const char kFCMInvalidationsDescription[];
+
 // Title and description for the command line switch used to determine the
 // active fullscreen viewport adjustment mode.
 extern const char kFullscreenViewportAdjustmentExperimentName[];
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 4e75b34..6b60454 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -25,6 +25,8 @@
     "ios_chrome_save_password_infobar_delegate.mm",
     "ios_chrome_update_password_infobar_delegate.h",
     "ios_chrome_update_password_infobar_delegate.mm",
+    "ios_password_infobar_controller.h",
+    "ios_password_infobar_controller.mm",
     "js_credential_manager.h",
     "js_credential_manager.mm",
     "notify_auto_signin_view_controller.h",
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
index 3931787..8d6c0e0a 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h
@@ -19,12 +19,15 @@
 
 // Base class for password manager infobar delegates, e.g.
 // IOSChromeSavePasswordInfoBarDelegate and
-// IOSChromeUpdatePasswordInfoBarDelegate. Provides link text and action for
-// smart lock.
+// IOSChromeUpdatePasswordInfoBarDelegate.
 class IOSChromePasswordManagerInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
   ~IOSChromePasswordManagerInfoBarDelegate() override;
 
+  // Getter for the message displayed in addition to the title. If no message
+  // was set, this returns an empty string.
+  base::string16 GetDetailsMessageText() const;
+
  protected:
   IOSChromePasswordManagerInfoBarDelegate(
       bool is_sync_user,
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
index 2c39a57..db638df 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.mm
@@ -8,7 +8,9 @@
 
 #include "base/strings/string16.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
+#include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -26,6 +28,12 @@
       infobar_response_(password_manager::metrics_util::NO_DIRECT_INTERACTION),
       is_sync_user_(is_sync_user) {}
 
+base::string16 IOSChromePasswordManagerInfoBarDelegate::GetDetailsMessageText()
+    const {
+  return is_sync_user_ ? l10n_util::GetStringUTF16(IDS_SAVE_PASSWORD_FOOTER)
+                       : base::string16();
+}
+
 int IOSChromePasswordManagerInfoBarDelegate::GetIconId() const {
   return IDR_IOS_INFOBAR_SAVE_PASSWORD;
 };
diff --git a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
index af5e4f0..d2ad368 100644
--- a/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.mm
@@ -13,6 +13,8 @@
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/infobars/infobar.h"
+#import "ios/chrome/browser/passwords/ios_password_infobar_controller.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -33,8 +35,11 @@
   auto delegate = base::WrapUnique(new IOSChromeSavePasswordInfoBarDelegate(
       is_sync_user, std::move(form_to_save)));
   delegate->set_dispatcher(dispatcher);
+  IOSPasswordInfoBarController* controller =
+      [[IOSPasswordInfoBarController alloc]
+          initWithInfoBarDelegate:delegate.get()];
   infobar_manager->AddInfoBar(
-      infobar_manager->CreateConfirmInfoBar(std::move(delegate)));
+      std::make_unique<InfoBarIOS>(controller, std::move(delegate)));
 }
 
 IOSChromeSavePasswordInfoBarDelegate::~IOSChromeSavePasswordInfoBarDelegate() {
diff --git a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
index 65ece545..e7eab4a 100644
--- a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
@@ -42,8 +42,8 @@
       [[UpdatePasswordInfoBarController alloc]
           initWithBaseViewController:baseViewController
                      infoBarDelegate:delegate.get()];
-  auto infobar = std::make_unique<InfoBarIOS>(controller, std::move(delegate));
-  infobar_manager->AddInfoBar(std::move(infobar));
+  infobar_manager->AddInfoBar(
+      std::make_unique<InfoBarIOS>(controller, std::move(delegate)));
 }
 
 IOSChromeUpdatePasswordInfoBarDelegate::
diff --git a/ios/chrome/browser/passwords/ios_password_infobar_controller.h b/ios/chrome/browser/passwords/ios_password_infobar_controller.h
new file mode 100644
index 0000000..2daf092
--- /dev/null
+++ b/ios/chrome/browser/passwords/ios_password_infobar_controller.h
@@ -0,0 +1,14 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_PASSWORDS_IOS_PASSWORD_INFOBAR_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_PASSWORDS_IOS_PASSWORD_INFOBAR_CONTROLLER_H_
+
+#import "ios/chrome/browser/infobars/confirm_infobar_controller.h"
+
+@interface IOSPasswordInfoBarController : ConfirmInfoBarController
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_PASSWORDS_IOS_PASSWORD_INFOBAR_CONTROLLER_H_
diff --git a/ios/chrome/browser/passwords/ios_password_infobar_controller.mm b/ios/chrome/browser/passwords/ios_password_infobar_controller.mm
new file mode 100644
index 0000000..7ba3ad3
--- /dev/null
+++ b/ios/chrome/browser/passwords/ios_password_infobar_controller.mm
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/passwords/ios_password_infobar_controller.h"
+
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/infobars/confirm_infobar_controller+protected.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h"
+#import "ios/chrome/browser/ui/infobars/confirm_infobar_view.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation IOSPasswordInfoBarController
+
+- (void)updateInfobarLabel:(ConfirmInfoBarView*)view {
+  [super updateInfobarLabel:view];
+
+  auto* delegate = static_cast<IOSChromePasswordManagerInfoBarDelegate*>(
+      self.infoBarDelegate);
+  base::string16 message = delegate->GetDetailsMessageText();
+  if (message.empty())
+    return;
+
+  [view addFooterLabel:base::SysUTF16ToNSString(
+                           delegate->GetDetailsMessageText())];
+}
+
+@end
diff --git a/ios/chrome/browser/passwords/update_password_infobar_controller.h b/ios/chrome/browser/passwords/update_password_infobar_controller.h
index 1845429..acb7d0ae 100644
--- a/ios/chrome/browser/passwords/update_password_infobar_controller.h
+++ b/ios/chrome/browser/passwords/update_password_infobar_controller.h
@@ -5,13 +5,13 @@
 #ifndef IOS_CHROME_BROWSER_PASSWORDS_UPDATE_PASSWORD_INFOBAR_CONTROLLER_H_
 #define IOS_CHROME_BROWSER_PASSWORDS_UPDATE_PASSWORD_INFOBAR_CONTROLLER_H_
 
-#include "ios/chrome/browser/infobars/confirm_infobar_controller.h"
+#import "ios/chrome/browser/passwords/ios_password_infobar_controller.h"
 
 class IOSChromeUpdatePasswordInfoBarDelegate;
 
 // Controller for the Update Password info bar. Presents an info bar that asks
 // the user whether they want to update their password.
-@interface UpdatePasswordInfoBarController : ConfirmInfoBarController
+@interface UpdatePasswordInfoBarController : IOSPasswordInfoBarController
 
 - (instancetype)init NS_UNAVAILABLE;
 
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index b5c8152..31a056b 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -54,7 +54,7 @@
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
-#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
@@ -193,7 +193,7 @@
 invalidation::InvalidationService*
 IOSChromeSyncClient::GetInvalidationService() {
   invalidation::ProfileInvalidationProvider* provider =
-      IOSChromeProfileInvalidationProviderFactory::GetForBrowserState(
+      IOSChromeDeprecatedProfileInvalidationProviderFactory::GetForBrowserState(
           browser_state_);
   if (provider)
     return provider->GetInvalidationService();
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index b11737b..ede0d70 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -24,7 +24,7 @@
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/gcm/ios_chrome_gcm_profile_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
-#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
@@ -111,7 +111,8 @@
   DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
   DependsOn(IOSChromePasswordStoreFactory::GetInstance());
-  DependsOn(IOSChromeProfileInvalidationProviderFactory::GetInstance());
+  DependsOn(
+      IOSChromeDeprecatedProfileInvalidationProviderFactory::GetInstance());
   DependsOn(ModelTypeStoreServiceFactory::GetInstance());
   DependsOn(ReadingListModelFactory::GetInstance());
   DependsOn(SessionSyncServiceFactory::GetInstance());
diff --git a/ios/chrome/browser/ui/app_launcher/app_launcher_coordinator_unittest.mm b/ios/chrome/browser/ui/app_launcher/app_launcher_coordinator_unittest.mm
index 6f5e8d1..2f66cca6 100644
--- a/ios/chrome/browser/ui/app_launcher/app_launcher_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/app_launcher/app_launcher_coordinator_unittest.mm
@@ -43,22 +43,6 @@
   id application_ = nil;
 };
 
-// Tests that an empty URL does not prompt user and does not launch application.
-TEST_F(AppLauncherCoordinatorTest, EmptyUrl) {
-  BOOL app_exists = [coordinator_ appLauncherTabHelper:nullptr
-                                      launchAppWithURL:GURL::EmptyGURL()
-                                        linkTransition:NO];
-  EXPECT_FALSE(app_exists);
-  EXPECT_EQ(nil, base_view_controller_.presentedViewController);
-}
-
-// Tests that an invalid URL does not launch application.
-TEST_F(AppLauncherCoordinatorTest, InvalidUrl) {
-  BOOL app_exists = [coordinator_ appLauncherTabHelper:nullptr
-                                      launchAppWithURL:GURL("invalid")
-                                        linkTransition:NO];
-  EXPECT_FALSE(app_exists);
-}
 
 // Tests that an itunes URL shows an alert.
 TEST_F(AppLauncherCoordinatorTest, ItmsUrlShowsAlert) {
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
index 02cced2d..6d18fe8 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
@@ -155,33 +155,23 @@
 }
 
 - (void)layoutSubviews {
+  // Set a bottom margin equal to the height of the secondary toolbar, if any.
+  // Deduct the bottom safe area inset as it is already included in the height
+  // of the secondary toolbar.
+  // TODO(crbug.com/894449): This won't update the infobar's position after the
+  // secondary toolbar reappears. Consider adding a constraint to the
+  // |layoutGuide| in |didMoveToSuperview|.
+  NamedGuide* layoutGuide =
+      [NamedGuide guideWithName:kSecondaryToolbarGuide view:self];
+  self.footerViewBottomAnchorConstraint.constant =
+      layoutGuide.layoutFrame.size.height;
+
   [super layoutSubviews];
 
   [self.sizingDelegate didSetInfoBarTargetHeight:CGRectGetHeight(self.frame)];
 }
 
-- (void)setFrame:(CGRect)frame {
-  [super setFrame:frame];
-
-  // Updates layout of subviews immediately, if layout updates are pending,
-  // rather than waiting for the next update cycle. Otherwise, the layout breaks
-  // on iPhone X.
-  // TODO(crbug.com/862688): Investigate why this is happening.
-  [self layoutIfNeeded];
-}
-
 - (CGSize)sizeThatFits:(CGSize)size {
-  // Set a bottom margin equal to the height of the secondary toolbar, if any.
-  // Deduct the bottom safe area inset as it is already included in the height
-  // of the secondary toolbar.
-  NamedGuide* layoutGuide =
-      [NamedGuide guideWithName:kSecondaryToolbarGuide view:self];
-  CGFloat bottomSafeAreaInset = SafeAreaInsetsForView(self).bottom;
-  self.footerViewBottomAnchorConstraint.constant =
-      layoutGuide.constrained
-          ? layoutGuide.layoutFrame.size.height - bottomSafeAreaInset
-          : 0;
-
   CGSize computedSize = [self systemLayoutSizeFittingSize:size];
   return CGSizeMake(size.width, computedSize.height);
 }
@@ -326,8 +316,8 @@
         UIEdgeInsetsMake(kButtonsTopPadding, kPadding, kPadding, kPadding);
     [self addSubview:footerView];
 
-    self.footerViewBottomAnchorConstraint = [safeAreaLayoutGuide.bottomAnchor
-        constraintEqualToAnchor:footerView.bottomAnchor];
+    self.footerViewBottomAnchorConstraint =
+        [self.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
     [NSLayoutConstraint activateConstraints:@[
       [safeAreaLayoutGuide.leadingAnchor
           constraintEqualToAnchor:footerView.leadingAnchor],
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index e77d34b0..e5f854a8 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -243,8 +243,6 @@
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/ui/app_rating_prompt.h"
 #include "ios/public/provider/chrome/browser/ui/default_ios_web_view_factory.h"
-#import "ios/public/provider/chrome/browser/voice/voice_search_bar.h"
-#import "ios/public/provider/chrome/browser/voice/voice_search_bar_owner.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_controller.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
 #import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
@@ -327,8 +325,6 @@
 // Duration of the toolbar animation.
 const NSTimeInterval kLegacyFullscreenControllerToolbarAnimationDuration = 0.3;
 
-const CGFloat kVoiceSearchBarHeight = 59.0;
-
 // Dimensions to use when downsizing an image for search-by-image.
 const CGFloat kSearchByImageMaxImageArea = 90000.0;
 const CGFloat kSearchByImageMaxImageWidth = 600.0;
@@ -451,8 +447,6 @@
                                     ToolbarHeightProviderForFullscreen,
                                     UIGestureRecognizerDelegate,
                                     UpgradeCenterClient,
-                                    VoiceSearchBarDelegate,
-                                    VoiceSearchBarOwner,
                                     WebStatePrinter> {
   // The dependency factory passed on initialization.  Used to vend objects used
   // by the BVC.
@@ -538,11 +532,6 @@
   // Bridge class to deliver container change notifications to BVC.
   std::unique_ptr<InfoBarContainerDelegateIOS> _infoBarContainerDelegate;
 
-  // TODO(crbug.com/800266): Remove this object.
-  // Voice search bar at the bottom of the view overlayed on |contentArea|
-  // when displaying voice search results.
-  UIView<VoiceSearchBar>* _voiceSearchBar;
-
   // The image fetcher used to save images and perform image-based searches.
   std::unique_ptr<image_fetcher::IOSImageDataFetcherWrapper> _imageFetcher;
 
@@ -656,8 +645,6 @@
 // Whether BVC prefers to hide the status bar. This value is used to determine
 // the response from the |prefersStatusBarHidden| method.
 @property(nonatomic, assign) BOOL hideStatusBar;
-// Whether the VoiceSearchBar should be displayed.
-@property(nonatomic, readonly) BOOL shouldShowVoiceSearchBar;
 // Coordinator for displaying a modal overlay with activity indicator to prevent
 // the user from interacting with the browser view.
 @property(nonatomic, strong)
@@ -880,8 +867,6 @@
 // ------------
 // Lazily instantiates |_voiceSearchController|.
 - (void)ensureVoiceSearchControllerCreated;
-// Shows/hides the voice search bar.
-- (void)updateVoiceSearchBarVisibilityAnimated:(BOOL)animated;
 
 // Reading List
 // ------------
@@ -1277,17 +1262,6 @@
   return _inNewTabAnimation;
 }
 
-- (BOOL)shouldShowVoiceSearchBar {
-  // On iPads, the voice search bar should only be shown for regular horizontal
-  // size class configurations.  It should always be shown for voice search
-  // results Tabs on iPhones, including configurations with regular horizontal
-  // size classes (i.e. landscape iPhone 6 Plus).
-  BOOL compactWidth = self.traitCollection.horizontalSizeClass ==
-                      UIUserInterfaceSizeClassCompact;
-  return self.tabModel.currentTab.isVoiceSearchResultsTab &&
-         (![self canShowTabStrip] || compactWidth);
-}
-
 - (void)setHideStatusBar:(BOOL)hideStatusBar {
   if (_hideStatusBar == hideStatusBar)
     return;
@@ -1522,7 +1496,6 @@
   [self ensureVoiceSearchControllerCreated];
 
   // Present voice search.
-  [_voiceSearchBar prepareToPresentVoiceSearch];
   _voiceSearchController->StartRecognition(self, [_model currentTab]);
   [self.dispatcher cancelOmniboxEdit];
 }
@@ -1852,8 +1825,6 @@
   // view controller).
   [self.presentedViewController
       traitCollectionDidChange:previousTraitCollection];
-  // Update voice search bar visibility.
-  [self updateVoiceSearchBarVisibilityAnimated:NO];
   // Change the height of the secondary toolbar to show/hide it.
   self.secondaryToolbarHeightConstraint.constant =
       [self secondaryToolbarHeightWithInset];
@@ -3045,22 +3016,6 @@
   }
 }
 
-- (void)updateVoiceSearchBarVisibilityAnimated:(BOOL)animated {
-  // Voice search bar exists and is shown/hidden.
-  BOOL show = self.shouldShowVoiceSearchBar;
-  if (_voiceSearchBar && _voiceSearchBar.hidden != show)
-    return;
-
-  // Voice search bar doesn't exist and thus is not visible.
-  if (!_voiceSearchBar && !show)
-    return;
-
-  if (animated)
-    [_voiceSearchBar animateToBecomeVisible:show];
-  else
-    _voiceSearchBar.hidden = !show;
-}
-
 #pragma mark - Private Methods: Reading List
 
 - (void)addToReadingListURL:(const GURL&)URL title:(NSString*)title {
@@ -3153,14 +3108,6 @@
     return @[];
 
   NSMutableArray* overlays = [NSMutableArray array];
-  UIView* voiceSearchView = [self voiceSearchOverlayViewForTab:tab];
-  if (voiceSearchView) {
-    CGFloat voiceSearchYOffset = [self voiceSearchOverlayYOffsetForTab:tab];
-    SnapshotOverlay* voiceSearchOverlay =
-        [[SnapshotOverlay alloc] initWithView:voiceSearchView
-                                      yOffset:voiceSearchYOffset];
-    [overlays addObject:voiceSearchOverlay];
-  }
   UIView* infoBarView = [self infoBarOverlayViewForTab:tab];
   if (infoBarView) {
     CGFloat infoBarYOffset = [self infoBarOverlayYOffsetForTab:tab];
@@ -3249,27 +3196,16 @@
          CGRectGetHeight(_downloadManagerCoordinator.viewController.view.frame);
 }
 
-// Provides a view that encompasses the voice search bar if it's displayed or
-// nil if the voice search bar isn't displayed.
-- (UIView*)voiceSearchOverlayViewForTab:(Tab*)tab {
-  Tab* currentTab = [_model currentTab];
-  if (tab && tab == currentTab && tab.isVoiceSearchResultsTab &&
-      _voiceSearchBar && ![_voiceSearchBar isHidden]) {
-    return _voiceSearchBar;
-  }
-  return nil;
-}
-
 // Returns a vertical voice search bar offset relative to the tab content.
 - (CGFloat)voiceSearchOverlayYOffsetForTab:(Tab*)tab {
-  if (tab != [_model currentTab] || [_voiceSearchBar isHidden]) {
+  if (tab != [_model currentTab]) {
     // There is no UI representation for non-current tabs or there is
     // no visible voice search. Return offset outside of tab.
     return CGRectGetMaxY(self.view.frame);
   } else {
     // The voice search bar on iPhone is displayed at the bottom of a tab.
     CGRect visibleFrame = [self visibleFrameForTab:_model.currentTab];
-    return CGRectGetMaxY(visibleFrame) - kVoiceSearchBarHeight;
+    return CGRectGetMaxY(visibleFrame);
   }
 }
 
@@ -4315,14 +4251,7 @@
 
   Tab* currentTab = [_model currentTab];
   DCHECK(currentTab);
-  BOOL wasVoiceSearchTab = currentTab.isVoiceSearchResultsTab;
   currentTab.navigationManager->LoadURLWithParams(params);
-  // When a Tab becomes a voice search Tab, the voice search bar doesn't need
-  // to be animated on screen because the transition animator will handle the
-  // animations.  When a Tab stops being a voice search Tab, the voice search
-  // bar should be animated away.
-  if (currentTab.isVoiceSearchResultsTab != wasVoiceSearchTab)
-    [self updateVoiceSearchBarVisibilityAnimated:wasVoiceSearchTab];
 }
 
 - (void)loadJavaScriptFromLocationBar:(NSString*)script {
@@ -4852,8 +4781,6 @@
     // no language selector presented.
     [_languageSelectionCoordinator dismissLanguageSelector];
   }
-  [self updateVoiceSearchBarVisibilityAnimated:NO];
-
   self.currentWebState->GetWebViewProxy().scrollViewProxy.clipsToBounds = NO;
 
   [_paymentRequestManager setActiveWebState:newTab.webState];
@@ -4878,12 +4805,6 @@
   }
 }
 
-- (void)tabModel:(TabModel*)model didStartLoadingTab:(Tab*)tab {
-  if (tab == [_model currentTab]) {
-    [self updateVoiceSearchBarVisibilityAnimated:NO];
-  }
-}
-
 - (void)tabModel:(TabModel*)model
     didFinishLoadingTab:(Tab*)tab
                 success:(BOOL)success {
@@ -5232,7 +5153,6 @@
   self.contentArea.frame = sideSwipeView.frame;
 
   [self.view insertSubview:self.contentArea aboveSubview:_fakeStatusBarView];
-  [self updateVoiceSearchBarVisibilityAnimated:NO];
   [self updateToolbar];
 
   // Reset horizontal stack view.
@@ -5264,7 +5184,6 @@
 
 - (void)updateAccessoryViewsForSideSwipeWithVisibility:(BOOL)visible {
   if (visible) {
-    [self updateVoiceSearchBarVisibilityAnimated:NO];
     [self updateToolbar];
     [_infoBarContainer->view() setHidden:NO];
   } else {
@@ -5272,7 +5191,6 @@
     // for welcome page.
     [self hideFindBarWithAnimation:NO];
     [_infoBarContainer->view() setHidden:YES];
-    [_voiceSearchBar setHidden:YES];
   }
 }
 
@@ -5389,45 +5307,9 @@
   _rateThisAppDialog = nil;
 }
 
-#pragma mark - VoiceSearchBarOwner
-
-- (id<VoiceSearchBar>)voiceSearchBar {
-  return _voiceSearchBar;
-}
-
-#pragma mark - VoiceSearchBarDelegate
-
-- (BOOL)isTTSEnabledForVoiceSearchBar:(id<VoiceSearchBar>)voiceSearchBar {
-  DCHECK_EQ(_voiceSearchBar, voiceSearchBar);
-  [self ensureVoiceSearchControllerCreated];
-  return _voiceSearchController->IsTextToSpeechEnabled() &&
-         _voiceSearchController->IsTextToSpeechSupported();
-}
-
-- (void)voiceSearchBarDidUpdateButtonState:(id<VoiceSearchBar>)voiceSearchBar {
-  DCHECK_EQ(_voiceSearchBar, voiceSearchBar);
-  SnapshotTabHelper::FromWebState(self.tabModel.currentTab.webState)
-      ->UpdateSnapshot(/*with_overlays=*/true, /*visible_frame_only=*/true);
-}
-
 #pragma mark - LogoAnimationControllerOwnerOwner (Public)
 
 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
-  return [self currentLogoAnimationControllerOwner];
-}
-
-#pragma mark - LogoAnimationControllerOwnerOwner helpers
-
-// The LogoAnimationControllerOwner to be used for the next logo transition
-// animation.
-- (id<LogoAnimationControllerOwner>)currentLogoAnimationControllerOwner {
-  Protocol* ownerProtocol = @protocol(LogoAnimationControllerOwner);
-  if ([_voiceSearchBar conformsToProtocol:ownerProtocol] &&
-      self.shouldShowVoiceSearchBar) {
-    // Use |_voiceSearchBar| for VoiceSearch results tab and dismissal
-    // animations.
-    return static_cast<id<LogoAnimationControllerOwner>>(_voiceSearchBar);
-  }
   id currentNativeController =
       [self nativeControllerForTab:self.tabModel.currentTab];
   Protocol* possibleOwnerProtocol =
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
index d2212f06..3fc5d52 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
@@ -29,7 +29,7 @@
 // Returns the height needed by a cell contained in |width| and containing the
 // listed informations.
 + (CGFloat)heightForWidth:(CGFloat)width
-                withImage:(BOOL)hasImage
+       withImageAvailable:(BOOL)hasImage
                     title:(NSString*)title
             publisherName:(NSString*)publisherName
           publicationDate:(NSString*)publicationDate;
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
index f19f6b7f..1f434a7 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h"
 
 #include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/util/i18n_string.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -48,8 +49,20 @@
 @property(nonatomic, strong) UIImageView* contentImageView;
 // Constraint for the size of the image.
 @property(nonatomic, strong) NSLayoutConstraint* imageSizeConstraint;
-// Constraint for the distance between the texts and the image.
-@property(nonatomic, strong) NSLayoutConstraint* imageTitleSpacing;
+// Constraint for the horizontal distance between the texts and the image
+// (standard content size).
+@property(nonatomic, strong) NSLayoutConstraint* imageTitleHorizontalSpacing;
+// Constraint for the vertical distance between the texts and the image
+// (accessibility content size).
+@property(nonatomic, strong) NSLayoutConstraint* imageTitleVerticalSpacing;
+
+// When they are activated, the image is on the leading side of the text.
+// They conflict with the accessibilityConstraints.
+@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* standardConstraints;
+// When they are activated, the image is above the text. The text is taking the
+// full width. They conflict with the standardConstraints.
+@property(nonatomic, strong)
+    NSArray<NSLayoutConstraint*>* accessibilityConstraints;
 
 // Applies the constraints on the elements. Called in the init.
 - (void)applyConstraints;
@@ -65,7 +78,6 @@
 @synthesize contentImageView = _contentImageView;
 @synthesize faviconView = _faviconView;
 @synthesize imageSizeConstraint = _imageSizeConstraint;
-@synthesize imageTitleSpacing = _imageTitleSpacing;
 @synthesize displayImage = _displayImage;
 
 - (instancetype)initWithFrame:(CGRect)frame {
@@ -152,11 +164,13 @@
 
 - (void)setDisplayImage:(BOOL)displayImage {
   if (displayImage) {
-    self.imageTitleSpacing.constant = kStandardSpacing;
+    self.imageTitleHorizontalSpacing.constant = kStandardSpacing;
+    self.imageTitleVerticalSpacing.constant = kStandardSpacing;
     self.imageSizeConstraint.constant = kImageSize;
     self.imageContainer.hidden = NO;
   } else {
-    self.imageTitleSpacing.constant = 0;
+    self.imageTitleHorizontalSpacing.constant = 0;
+    self.imageTitleVerticalSpacing.constant = 0;
     self.imageSizeConstraint.constant = 0;
     self.imageContainer.hidden = YES;
   }
@@ -164,31 +178,31 @@
 }
 
 + (CGFloat)heightForWidth:(CGFloat)width
-                withImage:(BOOL)hasImage
+       withImageAvailable:(BOOL)hasImage
                     title:(NSString*)title
             publisherName:(NSString*)publisherName
           publicationDate:(NSString*)publicationDate {
-    UILabel* titleLabel = [[UILabel alloc] init];
-    [self configureTitleLabel:titleLabel];
-    titleLabel.text = title;
+  UILabel* titleLabel = [[UILabel alloc] init];
+  [self configureTitleLabel:titleLabel];
+  titleLabel.text = title;
 
-    UILabel* additionalInfoLabel = [[UILabel alloc] init];
-    additionalInfoLabel.font = [self additionalInformationFont];
-    additionalInfoLabel.text =
-        [self stringForPublisher:publisherName date:publicationDate];
+  UILabel* additionalInfoLabel = [[UILabel alloc] init];
+  additionalInfoLabel.font = [self additionalInformationFont];
+  additionalInfoLabel.text =
+      [self stringForPublisher:publisherName date:publicationDate];
 
-    CGSize sizeForLabels =
-        CGSizeMake(width - [self labelMarginWithImage:hasImage], 500);
+  CGSize sizeForLabels =
+      CGSizeMake(width - [self labelHorizontalMarginsWithImage:hasImage], 500);
 
-    CGFloat minimalHeight = kImageSize + kStandardSpacing;
+  CGFloat minimalHeight = kImageSize + kStandardSpacing;
 
-    CGFloat labelHeight = kStandardSpacing;
-    labelHeight += [titleLabel sizeThatFits:sizeForLabels].height;
-    labelHeight += kSmallSpacing;
-    CGFloat additionalInfoHeight =
-        [additionalInfoLabel sizeThatFits:sizeForLabels].height;
-    labelHeight += MAX(additionalInfoHeight, kFaviconSize);
-    return MAX(minimalHeight, labelHeight);
+  CGFloat labelHeight = [titleLabel sizeThatFits:sizeForLabels].height;
+  labelHeight += [self labelVerticalMarginsWithImage:hasImage];
+  labelHeight += kSmallSpacing;
+  CGFloat additionalInfoHeight =
+      [additionalInfoLabel sizeThatFits:sizeForLabels].height;
+  labelHeight += MAX(additionalInfoHeight, kFaviconSize);
+  return MAX(minimalHeight, labelHeight);
 }
 
 #pragma mark - UICollectionViewCell
@@ -203,8 +217,24 @@
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
-  if (self.traitCollection.preferredContentSizeCategory !=
-      previousTraitCollection.preferredContentSizeCategory) {
+  UIContentSizeCategory currentCategory =
+      self.traitCollection.preferredContentSizeCategory;
+  UIContentSizeCategory previousCategory =
+      previousTraitCollection.preferredContentSizeCategory;
+
+  BOOL isCurrentCategoryAccessibility =
+      ContentSizeCategoryIsAccessibilityCategory(currentCategory);
+  if (isCurrentCategoryAccessibility !=
+      ContentSizeCategoryIsAccessibilityCategory(previousCategory)) {
+    if (isCurrentCategoryAccessibility) {
+      [NSLayoutConstraint deactivateConstraints:self.standardConstraints];
+      [NSLayoutConstraint activateConstraints:self.accessibilityConstraints];
+    } else {
+      [NSLayoutConstraint deactivateConstraints:self.accessibilityConstraints];
+      [NSLayoutConstraint activateConstraints:self.standardConstraints];
+    }
+  }
+  if (currentCategory != previousCategory) {
     [[self class] configureTitleLabel:_titleLabel];
     _additionalInformationLabel.font = [[self class] additionalInformationFont];
   }
@@ -220,7 +250,8 @@
   CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
 
   self.titleLabel.preferredMaxLayoutWidth =
-      parentWidth - [[self class] labelMarginWithImage:self.displayImage];
+      parentWidth -
+      [[self class] labelHorizontalMarginsWithImage:self.displayImage];
   self.additionalInformationLabel.preferredMaxLayoutWidth =
       parentWidth - kFaviconSize - kSmallSpacing - 2 * kStandardSpacing;
 
@@ -234,9 +265,6 @@
 - (void)applyConstraints {
   _imageSizeConstraint =
       [_imageContainer.heightAnchor constraintEqualToConstant:kImageSize];
-  _imageTitleSpacing = [_titleLabel.leadingAnchor
-      constraintEqualToAnchor:_imageContainer.trailingAnchor
-                     constant:kStandardSpacing];
 
   [NSLayoutConstraint activateConstraints:@[
     // Image.
@@ -245,10 +273,6 @@
         constraintLessThanOrEqualToAnchor:_faviconView.bottomAnchor],
     [_imageContainer.widthAnchor
         constraintEqualToAnchor:_imageContainer.heightAnchor],
-    [_imageContainer.topAnchor constraintEqualToAnchor:_titleLabel.topAnchor],
-
-    // Text.
-    _imageTitleSpacing,
 
     // Additional Information.
     [_additionalInformationLabel.trailingAnchor
@@ -277,11 +301,36 @@
 
   AddSameConstraints(_contentImageView, _imageContainer);
 
+  _imageTitleHorizontalSpacing = [_titleLabel.leadingAnchor
+      constraintEqualToAnchor:_imageContainer.trailingAnchor
+                     constant:kStandardSpacing];
+  _imageTitleVerticalSpacing = [_titleLabel.topAnchor
+      constraintEqualToAnchor:_imageContainer.bottomAnchor
+                     constant:kStandardSpacing];
+  _standardConstraints = @[
+    _imageTitleHorizontalSpacing,
+    [_titleLabel.topAnchor constraintEqualToAnchor:_imageContainer.topAnchor],
+  ];
+
+  _accessibilityConstraints = @[
+    [_titleLabel.leadingAnchor
+        constraintEqualToAnchor:self.contentView.leadingAnchor
+                       constant:kStandardSpacing],
+    _imageTitleVerticalSpacing,
+  ];
+
+  if (ContentSizeCategoryIsAccessibilityCategory(
+          self.traitCollection.preferredContentSizeCategory)) {
+    [NSLayoutConstraint activateConstraints:self.accessibilityConstraints];
+  } else {
+    [NSLayoutConstraint activateConstraints:self.standardConstraints];
+  }
+
   ApplyVisualConstraintsWithMetrics(
       @[
         @"H:[title]-(space)-|",
         @"H:|-(space)-[image]",
-        @"V:|-(space)-[title]",
+        @"V:|-(space)-[image]",
         @"H:[favicon]-(small)-[additional]",
       ],
       @{
@@ -313,12 +362,32 @@
   return [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2];
 }
 
-// Returns the margin for the labels, depending if the cell |hasImage|.
-+ (CGFloat)labelMarginWithImage:(BOOL)hasImage {
-  CGFloat offset = hasImage ? kImageSize + kStandardSpacing : 0;
+// Returns the horizontal margin for the labels, depending if the cell
+// |hasImage| and the content size category.
++ (CGFloat)labelHorizontalMarginsWithImage:(BOOL)hasImage {
+  BOOL isCurrentCategoryAccessibility =
+      ContentSizeCategoryIsAccessibilityCategory(
+          [UIApplication sharedApplication].preferredContentSizeCategory);
+
+  CGFloat offset = (hasImage && !isCurrentCategoryAccessibility)
+                       ? kImageSize + kStandardSpacing
+                       : 0;
   return 2 * kStandardSpacing + offset;
 }
 
+// Returns the vertical margin for the labels, depending if the cell |hasImage|
+// and the content size category.
++ (CGFloat)labelVerticalMarginsWithImage:(BOOL)hasImage {
+  BOOL isCurrentCategoryAccessibility =
+      ContentSizeCategoryIsAccessibilityCategory(
+          [UIApplication sharedApplication].preferredContentSizeCategory);
+
+  CGFloat offset = (hasImage && isCurrentCategoryAccessibility)
+                       ? kImageSize + kStandardSpacing
+                       : 0;
+  return kStandardSpacing + offset;
+}
+
 // Returns the attributed string to be displayed.
 + (NSString*)stringForPublisher:(NSString*)publisherName date:(NSString*)date {
   return AdjustStringForLocaleDirection(
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
index 89223ea..90f4454a 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
@@ -93,7 +93,7 @@
 
 - (CGFloat)cellHeightForWidth:(CGFloat)width {
   return [self.cellClass heightForWidth:width
-                              withImage:self.hasImage
+                     withImageAvailable:self.hasImage
                                   title:self.title
                           publisherName:self.publisher
                         publicationDate:[self relativeDate]];
diff --git a/ios/chrome/browser/ui/infobars/confirm_infobar_view.h b/ios/chrome/browser/ui/infobars/confirm_infobar_view.h
index ae6d1850..5c19fce 100644
--- a/ios/chrome/browser/ui/infobars/confirm_infobar_view.h
+++ b/ios/chrome/browser/ui/infobars/confirm_infobar_view.h
@@ -60,6 +60,9 @@
                     target:(id)target
                     action:(SEL)action;
 
+// Adds to the infobar a footer label below the title.
+- (void)addFooterLabel:(NSString*)label;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_INFOBARS_CONFIRM_INFOBAR_VIEW_H_
diff --git a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
index 6262707..d620558c 100644
--- a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
+++ b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
@@ -113,11 +113,79 @@
 
 }  // namespace
 
-// UIView containing a switch and a label.
-@interface SwitchView : BidiContainerView
+// UIView containing a label.
+@interface InfobarFooterView : BidiContainerView
+
+@property(nonatomic, readonly) UILabel* label;
+@property(nonatomic) CGFloat preferredLabelWidth;
 
 // Initialize the view's label with |labelText|.
-- (id)initWithLabel:(NSString*)labelText isOn:(BOOL)isOn;
+- (instancetype)initWithText:(NSString*)labelText NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
+
+// Returns the height taken by the view constrained by a width of |width|.
+// If |layout| is yes, it sets the frame of the view to fit |width|.
+- (CGFloat)heightRequiredForFooterWithWidth:(CGFloat)width layout:(BOOL)layout;
+
+// Returns the preferred width. A smaller width requires eliding the text.
+- (CGFloat)preferredWidth;
+@end
+
+@implementation InfobarFooterView
+
+- (instancetype)initWithText:(NSString*)labelText {
+  // Creates label.
+  UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
+  label.textAlignment = NSTextAlignmentNatural;
+  label.font = InfoBarSwitchLabelFont();
+  label.text = labelText;
+  label.textColor = [UIColor darkGrayColor];
+  label.backgroundColor = [UIColor clearColor];
+  label.lineBreakMode = NSLineBreakByWordWrapping;
+  label.numberOfLines = 0;
+  label.adjustsFontSizeToFitWidth = NO;
+  [label sizeToFit];
+
+  self = [super initWithFrame:label.frame];
+  if (!self)
+    return nil;
+  _label = label;
+  _preferredLabelWidth = CGRectGetMaxX(_label.frame);
+  [self addSubview:_label];
+  return self;
+}
+
+- (CGFloat)heightRequiredForFooterWithWidth:(CGFloat)width layout:(BOOL)layout {
+  CGFloat widthLeftForLabel = width;
+  CGSize maxSize = CGSizeMake(widthLeftForLabel, CGFLOAT_MAX);
+  CGSize labelSize =
+      [[self.label text] cr_boundingSizeWithSize:maxSize
+                                            font:[self.label font]];
+  CGFloat viewHeight = labelSize.height;
+  if (layout) {
+    // Lays out the label and the switch to fit in {width, viewHeight}.
+    CGRect newLabelFrame = CGRectMake(0, 0, labelSize.width, labelSize.height);
+    newLabelFrame = AlignRectOriginAndSizeToPixels(newLabelFrame);
+    [self.label setFrame:newLabelFrame];
+  }
+  return viewHeight;
+}
+
+- (CGFloat)preferredWidth {
+  return self.preferredLabelWidth;
+}
+
+@end
+
+// UIView containing a switch and a label.
+@interface SwitchView : InfobarFooterView
+
+// Initialize the view's label with |labelText|.
+- (instancetype)initWithText:(NSString*)labelText
+                        isOn:(BOOL)isOn NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithText:(NSString*)labelText NS_UNAVAILABLE;
 
 // Specifies the object, action, and tag used when the switch is toggled.
 - (void)setTag:(NSInteger)tag target:(id)target action:(SEL)action;
@@ -125,7 +193,7 @@
 // Returns the height taken by the view constrained by a width of |width|.
 // If |layout| is yes, it sets the frame of the label and the switch to fit
 // |width|.
-- (CGFloat)heightRequiredForSwitchWithWidth:(CGFloat)width layout:(BOOL)layout;
+- (CGFloat)heightRequiredForFooterWithWidth:(CGFloat)width layout:(BOOL)layout;
 
 // Returns the preferred width. A smaller width requires eliding the text.
 - (CGFloat)preferredWidth;
@@ -133,46 +201,34 @@
 @end
 
 @implementation SwitchView {
-  UILabel* label_;
   UISwitch* switch_;
   CGFloat preferredTotalWidth_;
-  CGFloat preferredLabelWidth_;
   // Layout metrics for calculating item placement.
   const LayoutMetrics* metrics_;
 }
 
-- (id)initWithLabel:(NSString*)labelText isOn:(BOOL)isOn {
+- (instancetype)initWithText:(NSString*)labelText isOn:(BOOL)isOn {
   metrics_ = &kLayoutMetrics;
 
-  // Creates switch and label.
-  UILabel* tempLabel = [[UILabel alloc] initWithFrame:CGRectZero];
-  [tempLabel setTextAlignment:NSTextAlignmentNatural];
-  [tempLabel setFont:InfoBarSwitchLabelFont()];
-  [tempLabel setText:labelText];
-  [tempLabel setBackgroundColor:[UIColor clearColor]];
-  [tempLabel setLineBreakMode:NSLineBreakByWordWrapping];
-  [tempLabel setNumberOfLines:0];
-  [tempLabel setAdjustsFontSizeToFitWidth:NO];
-  UISwitch* tempSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
-  [tempSwitch setExclusiveTouch:YES];
-  [tempSwitch setAccessibilityLabel:labelText];
-  [tempSwitch setOnTintColor:[[MDCPalette cr_bluePalette] tint500]];
-  [tempSwitch setOn:isOn];
+  self = [super initWithText:labelText];
+  if (!self)
+    return nil;
+
+  self.label.textColor = [UIColor blackColor];
+  switch_ = [[UISwitch alloc] initWithFrame:CGRectZero];
+  switch_.exclusiveTouch = YES;
+  switch_.accessibilityLabel = labelText;
+  switch_.onTintColor = [[MDCPalette cr_bluePalette] tint500];
+  switch_.on = isOn;
 
   // Computes the size and initializes the view.
-  CGSize maxSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
-  CGSize labelSize =
-      [[tempLabel text] cr_boundingSizeWithSize:maxSize font:[tempLabel font]];
-  CGSize switchSize = [tempSwitch frame].size;
+  CGSize labelSize = self.label.frame.size;
+  CGSize switchSize = switch_.frame.size;
   CGRect frameRect = CGRectMake(
       0, 0,
       labelSize.width + metrics_->space_between_widgets + switchSize.width,
       std::max(labelSize.height, switchSize.height));
-  self = [super initWithFrame:frameRect];
-  if (!self)
-    return nil;
-  label_ = tempLabel;
-  switch_ = tempSwitch;
+  [self setFrame:frameRect];
 
   // Sets the position of the label and the switch. The label is left aligned
   // and the switch is right aligned. Both are vertically centered.
@@ -187,12 +243,12 @@
   labelFrame = AlignRectOriginAndSizeToPixels(labelFrame);
   switchFrame = AlignRectOriginAndSizeToPixels(switchFrame);
 
-  [label_ setFrame:labelFrame];
+  [self.label setFrame:labelFrame];
   [switch_ setFrame:switchFrame];
   preferredTotalWidth_ = CGRectGetMaxX(switchFrame);
-  preferredLabelWidth_ = CGRectGetMaxX(labelFrame);
+  self.preferredLabelWidth = CGRectGetMaxX(labelFrame);
 
-  [self addSubview:label_];
+  [self addSubview:self.label];
   [self addSubview:switch_];
   return self;
 }
@@ -204,12 +260,13 @@
       forControlEvents:UIControlEventValueChanged];
 }
 
-- (CGFloat)heightRequiredForSwitchWithWidth:(CGFloat)width layout:(BOOL)layout {
+- (CGFloat)heightRequiredForFooterWithWidth:(CGFloat)width layout:(BOOL)layout {
   CGFloat widthLeftForLabel =
       width - [switch_ frame].size.width - metrics_->space_between_widgets;
   CGSize maxSize = CGSizeMake(widthLeftForLabel, CGFLOAT_MAX);
   CGSize labelSize =
-      [[label_ text] cr_boundingSizeWithSize:maxSize font:[label_ font]];
+      [[self.label text] cr_boundingSizeWithSize:maxSize
+                                            font:[self.label font]];
   CGFloat viewHeight = std::max(labelSize.height, [switch_ frame].size.height);
   if (layout) {
     // Lays out the label and the switch to fit in {width, viewHeight}.
@@ -218,7 +275,7 @@
     newLabelFrame.origin.y = (viewHeight - labelSize.height) / 2;
     newLabelFrame.size = labelSize;
     newLabelFrame = AlignRectOriginAndSizeToPixels(newLabelFrame);
-    [label_ setFrame:newLabelFrame];
+    [self.label setFrame:newLabelFrame];
     CGRect newSwitchFrame;
     newSwitchFrame.origin.x =
         CGRectGetMaxX(newLabelFrame) + metrics_->space_between_widgets;
@@ -282,8 +339,8 @@
   UIImageView* imageView_;
   // Close button.
   UIButton* closeButton_;
-  // View containing the switch and its label.
-  SwitchView* switchView_;
+  // View containing the label and maybe switch.
+  InfobarFooterView* InfobarFooterView_;
   // We are using a LabelLinkController with an UILabel to be able to have
   // parts of the label underlined and clickable. This label_ may be nil if
   // the delegate returns an empty string for GetMessageText().
@@ -386,14 +443,14 @@
 
 // Returns the width needed for the switch.
 - (CGFloat)preferredWidthOfSwitch {
-  return [switchView_ preferredWidth];
+  return [InfobarFooterView_ preferredWidth];
 }
 
 // Returns the space required to separate the left aligned widgets (label) from
 // the right aligned widgets (switch, buttons), assuming they fit on one line.
 - (CGFloat)widthToSeparateRightAndLeftWidgets {
   BOOL leftWidgetsArePresent = (label_ != nil);
-  BOOL rightWidgetsArePresent = button1_ || button2_ || switchView_;
+  BOOL rightWidgetsArePresent = button1_ || button2_ || InfobarFooterView_;
   if (!leftWidgetsArePresent || !rightWidgetsArePresent)
     return 0;
   return metrics_->minimum_space_between_right_and_left_aligned_widgets;
@@ -402,7 +459,7 @@
 // Returns the space required to separate the switch and the buttons.
 - (CGFloat)widthToSeparateSwitchAndButtons {
   BOOL buttonsArePresent = button1_ || button2_;
-  BOOL switchIsPresent = (switchView_ != nil);
+  BOOL switchIsPresent = (InfobarFooterView_ != nil);
   if (!buttonsArePresent || !switchIsPresent)
     return 0;
   return metrics_->space_between_widgets;
@@ -564,15 +621,15 @@
         [button2_ setFrame:frame];
       }
       // Lays out the switch view to the left of the buttons.
-      if (switchView_) {
-        frame =
-            CGRectMake(widthOfScreen - buttonMargin - widthOfButtonAndSwitch,
-                       (metrics_->minimum_infobar_height -
-                        [switchView_ frame].size.height) /
-                           2.0,
-                       preferredWidthOfSwitch, [switchView_ frame].size.height);
+      if (InfobarFooterView_) {
+        frame = CGRectMake(
+            widthOfScreen - buttonMargin - widthOfButtonAndSwitch,
+            (metrics_->minimum_infobar_height -
+             [InfobarFooterView_ frame].size.height) /
+                2.0,
+            preferredWidthOfSwitch, [InfobarFooterView_ frame].size.height);
         frame = AlignRectOriginAndSizeToPixels(frame);
-        [switchView_ setFrame:frame];
+        [InfobarFooterView_ setFrame:frame];
       }
     }
   } else {
@@ -598,13 +655,15 @@
                        [self widthOfLabelOnASingleLine], labelHeight);
         labelFrame = AlignRectOriginAndSizeToPixels(labelFrame);
         [label_ setFrame:labelFrame];
-        if (switchView_) {
+        if (InfobarFooterView_) {
           CGRect switchRect = CGRectMake(
               widthOfScreen - rightMarginOnFirstLine - preferredWidthOfSwitch,
-              (heightOfLabelAndSwitch - [switchView_ frame].size.height) / 2,
-              preferredWidthOfSwitch, [switchView_ frame].size.height);
+              (heightOfLabelAndSwitch -
+               [InfobarFooterView_ frame].size.height) /
+                  2,
+              preferredWidthOfSwitch, [InfobarFooterView_ frame].size.height);
           switchRect = AlignRectOriginAndSizeToPixels(switchRect);
-          [switchView_ setFrame:switchRect];
+          [InfobarFooterView_ setFrame:switchRect];
         }
       }
     } else {
@@ -632,14 +691,14 @@
       // Computes the height of the switch view (if any), and optionally lays it
       // out.
       CGFloat heightOfSwitchWithPadding = 0;
-      if (switchView_ != nil) {
+      if (InfobarFooterView_ != nil) {
         // The switch view is aligned with the first line's label, hence the
         // call to |leftMarginOnFirstLine|.
         CGFloat widthAvailableForSwitchView = [self frame].size.width -
                                               [self leftMarginOnFirstLine] -
                                               metrics_->right_margin;
-        CGFloat heightOfSwitch = [switchView_
-            heightRequiredForSwitchWithWidth:widthAvailableForSwitchView
+        CGFloat heightOfSwitch = [InfobarFooterView_
+            heightRequiredForFooterWithWidth:widthAvailableForSwitchView
                                       layout:layout];
         // If there are buttons underneath the switch, add padding.
         if (button1_ || button2_) {
@@ -654,7 +713,7 @@
               CGRectMake([self leftMarginOnFirstLine], heightOfLabelWithPadding,
                          widthAvailableForSwitchView, heightOfSwitch);
           switchRect = AlignRectOriginAndSizeToPixels(switchRect);
-          [switchView_ setFrame:switchRect];
+          [InfobarFooterView_ setFrame:switchRect];
         }
       }
       heightOfLabelAndSwitch =
@@ -749,9 +808,15 @@
                        tag:(NSInteger)tag
                     target:(id)target
                     action:(SEL)action {
-  switchView_ = [[SwitchView alloc] initWithLabel:label isOn:isOn];
-  [switchView_ setTag:tag target:target action:action];
-  [self addSubview:switchView_];
+  SwitchView* switchView = [[SwitchView alloc] initWithText:label isOn:isOn];
+  [switchView setTag:tag target:target action:action];
+  InfobarFooterView_ = switchView;
+  [self addSubview:InfobarFooterView_];
+}
+
+- (void)addFooterLabel:(NSString*)label {
+  InfobarFooterView_ = [[InfobarFooterView alloc] initWithText:label];
+  [self addSubview:InfobarFooterView_];
 }
 
 - (void)addLeftIcon:(UIImage*)image {
diff --git a/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm b/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
index d7b1414..a41cee13 100644
--- a/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.mm
@@ -235,6 +235,10 @@
                                                           self.isStarred);
 }
 
+- (BOOL)isTabMatch {
+  return _match.has_tab_match;
+}
+
 #pragma mark helpers
 
 // Create a string to display for an answer line.
diff --git a/ios/chrome/browser/ui/omnibox/autocomplete_suggestion.h b/ios/chrome/browser/ui/omnibox/autocomplete_suggestion.h
index c908da7..12fb95cd 100644
--- a/ios/chrome/browser/ui/omnibox/autocomplete_suggestion.h
+++ b/ios/chrome/browser/ui/omnibox/autocomplete_suggestion.h
@@ -28,6 +28,8 @@
 // image is in template rendering mode, it is expected to be tinted by the image
 // view.
 - (UIImage*)suggestionTypeIcon;
+// Some suggestions are opened in an other tab.
+- (BOOL)isTabMatch;
 
 // Text of the suggestion.
 - (NSAttributedString*)text;
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index 80ce26a..5b494f97 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -88,6 +88,7 @@
     "//ios/chrome/browser/ui/tab_grid/transitions",
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/common/ui_util",
+    "//ios/web/public",
     "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/uikit_ui_util.h b/ios/chrome/browser/ui/uikit_ui_util.h
index 976bf18..46b29de 100644
--- a/ios/chrome/browser/ui/uikit_ui_util.h
+++ b/ios/chrome/browser/ui/uikit_ui_util.h
@@ -248,4 +248,8 @@
 // more than 99 tabs open.
 NSString* TextForTabCount(long count);
 
+// Helper check if |category| is an accessibility category. For iOS 11+ it is a
+// wrapper around UIContentSizeCategoryIsAccessibilityCategory.
+BOOL ContentSizeCategoryIsAccessibilityCategory(UIContentSizeCategory category);
+
 #endif  // IOS_CHROME_BROWSER_UI_UIKIT_UI_UTIL_H_
diff --git a/ios/chrome/browser/ui/uikit_ui_util.mm b/ios/chrome/browser/ui/uikit_ui_util.mm
index e04ba59..51aa794 100644
--- a/ios/chrome/browser/ui/uikit_ui_util.mm
+++ b/ios/chrome/browser/ui/uikit_ui_util.mm
@@ -679,3 +679,16 @@
     return @":)";
   return [NSString stringWithFormat:@"%ld", count];
 }
+
+BOOL ContentSizeCategoryIsAccessibilityCategory(
+    UIContentSizeCategory category) {
+  if (@available(iOS 11.0, *)) {
+    return UIContentSizeCategoryIsAccessibilityCategory(category);
+  } else {
+    return category == UIContentSizeCategoryAccessibilityExtraExtraExtraLarge ||
+           category == UIContentSizeCategoryAccessibilityExtraExtraLarge ||
+           category == UIContentSizeCategoryAccessibilityExtraLarge ||
+           category == UIContentSizeCategoryAccessibilityLarge ||
+           category == UIContentSizeCategoryAccessibilityMedium;
+  }
+}
diff --git a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.h b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.h
index b60afcf..832dcce0 100644
--- a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.h
+++ b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.h
@@ -30,6 +30,10 @@
   // Cancels any pending distribution notifications.
   virtual void CancelDistributionNotifications();
 
+  // Returns whether user who installed Chrome on |install_date| predates
+  // integration with Firebase for installation attribution.
+  virtual bool IsPreFirebaseLegacyUser(int64_t install_date);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AppDistributionProvider);
 };
diff --git a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
index 3f744451..39827c9 100644
--- a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
+++ b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
@@ -21,3 +21,7 @@
     bool is_first_run) {}
 
 void AppDistributionProvider::CancelDistributionNotifications() {}
+
+bool AppDistributionProvider::IsPreFirebaseLegacyUser(int64_t install_date) {
+  return false;
+}
diff --git a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h
index 7efef29..e620c4e 100644
--- a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h
+++ b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h
@@ -25,6 +25,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
       bool is_first_run) override;
   void CancelDistributionNotifications() override;
+  bool IsPreFirebaseLegacyUser(int64_t install_date) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestAppDistributionProvider);
diff --git a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
index eb89745..959d597 100644
--- a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
+++ b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
@@ -21,3 +21,8 @@
     bool is_first_run) {}
 
 void TestAppDistributionProvider::CancelDistributionNotifications() {}
+
+bool TestAppDistributionProvider::IsPreFirebaseLegacyUser(
+    int64_t install_date) {
+  return false;
+}
diff --git a/ios/public/provider/chrome/browser/voice/BUILD.gn b/ios/public/provider/chrome/browser/voice/BUILD.gn
index 0e67bbdf..217f919 100644
--- a/ios/public/provider/chrome/browser/voice/BUILD.gn
+++ b/ios/public/provider/chrome/browser/voice/BUILD.gn
@@ -7,8 +7,6 @@
   sources = [
     "audio_session_controller.h",
     "logo_animation_controller.h",
-    "voice_search_bar.h",
-    "voice_search_bar_owner.h",
     "voice_search_controller.h",
     "voice_search_controller.mm",
     "voice_search_language.h",
diff --git a/ios/public/provider/chrome/browser/voice/voice_search_bar.h b/ios/public/provider/chrome/browser/voice/voice_search_bar.h
deleted file mode 100644
index f6591d2..0000000
--- a/ios/public/provider/chrome/browser/voice/voice_search_bar.h
+++ /dev/null
@@ -1,42 +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_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_H_
-#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_H_
-
-#import <Foundation/Foundation.h>
-
-@protocol VoiceSearchBarDelegate;
-
-// TODO(crbug.com/800266): Check if those protocols are still relevant with the
-// adaptive toolbar.
-
-// Protocol used by bottom toolbars containing a button to launch VoiceSearch.
-@protocol VoiceSearchBar<NSObject>
-
-// The VoiceSearchBar's delegate.
-@property(nonatomic, assign) id<VoiceSearchBarDelegate> voiceSearchBarDelegate;
-
-// Animates the view up from the bottom of the its superview so that it's
-// visible or down so that it's no longer visible.
-- (void)animateToBecomeVisible:(BOOL)visible;
-
-// Sets necessary state to prepare for presenting voice search.
-- (void)prepareToPresentVoiceSearch;
-
-@end
-
-// The delegate protocol for VoiceSearchBar.
-@protocol VoiceSearchBarDelegate
-
-// Returns whether Text To Speech is enabled for |voiceSearchBar|.
-- (BOOL)isTTSEnabledForVoiceSearchBar:(id<VoiceSearchBar>)voiceSearchBar;
-
-// Called to notify the delegate that the state of |voiceSearchBar|'s
-// VoiceSearch button appearance has been updated.
-- (void)voiceSearchBarDidUpdateButtonState:(id<VoiceSearchBar>)voiceSearchBar;
-
-@end
-
-#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_H_
diff --git a/ios/public/provider/chrome/browser/voice/voice_search_bar_owner.h b/ios/public/provider/chrome/browser/voice/voice_search_bar_owner.h
deleted file mode 100644
index 5ff7704..0000000
--- a/ios/public/provider/chrome/browser/voice/voice_search_bar_owner.h
+++ /dev/null
@@ -1,20 +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_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_OWNER_H_
-#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_OWNER_H_
-
-#import <Foundation/Foundation.h>
-
-@protocol VoiceSearchBar;
-
-// Protocol implemented by UIViewControllers that own a VoiceSearchBar.
-@protocol VoiceSearchBarOwner<NSObject>
-
-// The VoiceSearchBar.
-@property(nonatomic, readonly) id<VoiceSearchBar> voiceSearchBar;
-
-@end
-
-#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_VOICE_VOICE_SEARCH_BAR_OWNER_H_
diff --git a/ios/public/provider/chrome/browser/voice/voice_search_provider.h b/ios/public/provider/chrome/browser/voice/voice_search_provider.h
index b47ee07..92b4550 100644
--- a/ios/public/provider/chrome/browser/voice/voice_search_provider.h
+++ b/ios/public/provider/chrome/browser/voice/voice_search_provider.h
@@ -13,7 +13,6 @@
 
 @protocol ApplicationCommands;
 class AudioSessionController;
-@protocol VoiceSearchBar;
 class VoiceSearchController;
 
 namespace ios {
@@ -41,15 +40,6 @@
   virtual scoped_refptr<VoiceSearchController> CreateVoiceSearchController(
       ios::ChromeBrowserState* browser_state) const;
 
-  // Creates a new VoiceSearchBar.  Returns an autoreleased view.
-  virtual UIView<VoiceSearchBar>* BuildVoiceSearchBar(CGRect frame) const;
-
-  // Creates a new VoiceSearchBar which uses |dispatcher| to send commands.
-  // Returns an autoreleased view.
-  virtual UIView<VoiceSearchBar>* BuildVoiceSearchBar(
-      CGRect frame,
-      id<ApplicationCommands> dispatcher) const;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(VoiceSearchProvider);
 };
diff --git a/ios/public/provider/chrome/browser/voice/voice_search_provider.mm b/ios/public/provider/chrome/browser/voice/voice_search_provider.mm
index 52d979d..1db4e98 100644
--- a/ios/public/provider/chrome/browser/voice/voice_search_provider.mm
+++ b/ios/public/provider/chrome/browser/voice/voice_search_provider.mm
@@ -28,13 +28,3 @@
   return scoped_refptr<VoiceSearchController>(nullptr);
 }
 
-UIView<VoiceSearchBar>* VoiceSearchProvider::BuildVoiceSearchBar(
-    CGRect frame) const {
-  return nil;
-}
-
-UIView<VoiceSearchBar>* VoiceSearchProvider::BuildVoiceSearchBar(
-    CGRect frame,
-    id<ApplicationCommands> dispatcher) const {
-  return BuildVoiceSearchBar(frame);
-}
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
index 5dff91a..89e19bc 100644
--- a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
+++ b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
@@ -50,7 +50,7 @@
 
 - (CGFloat)cellHeightForWidth:(CGFloat)width {
   return [self.cellClass heightForWidth:width
-                              withImage:self.hasImage
+                     withImageAvailable:self.hasImage
                                   title:self.title
                           publisherName:self.publisher
                         publicationDate:self.publicationDate];
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index c1fc438..38e7bb1 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -231,13 +231,9 @@
     ]
 
     libs += [
-      "dmoguids.lib",
       "dxguid.lib",
-      "msdmo.lib",
       "setupapi.lib",
-      "strmiids.lib",
       "winmm.lib",
-      "wmcodecdspuuid.lib",
     ]
   }
 
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc
index 1cce7d614..90b1215 100644
--- a/media/audio/win/audio_low_latency_input_win.cc
+++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -4,11 +4,7 @@
 
 #include "media/audio/win/audio_low_latency_input_win.h"
 
-#include <audiopolicy.h>
-#include <mediaobj.h>
 #include <objbase.h>
-#include <uuids.h>
-#include <wmcodecdsp.h>
 
 #include <algorithm>
 #include <cmath>
@@ -70,125 +66,18 @@
   return true;
 }
 
-// Returns the index of the device in the device collection, or -1 for the
-// default device, as used by the voice processing DMO.
-base::Optional<WORD> GetAudioDeviceCollectionIndexFromId(
-    const std::string& device_id,
-    const EDataFlow data_flow) {
-  // The default device is specified with -1.
-  if (AudioDeviceDescription::IsDefaultDevice(device_id))
-    return -1;
-
-  WORD device_index = -1;
-  HRESULT hr = E_FAIL;
-  // The default communications does not have an index itself, so we need to
-  // find the index for the underlying device.
-  if (AudioDeviceDescription::IsCommunicationsDevice(device_id)) {
-    const std::string communications_id =
-        (data_flow == eCapture)
-            ? CoreAudioUtil::GetCommunicationsInputDeviceID()
-            : CoreAudioUtil::GetCommunicationsOutputDeviceID();
-    hr = CoreAudioUtil::GetDeviceCollectionIndex(communications_id, data_flow,
-                                                 &device_index);
-  } else {
-    // Otherwise, just look for the device_id directly.
-    hr = CoreAudioUtil::GetDeviceCollectionIndex(device_id, data_flow,
-                                                 &device_index);
-  }
-
-  if (FAILED(hr) || hr == S_FALSE)
-    return base::nullopt;
-
-  return device_index;
-}
-
-// Implementation of IMediaBuffer, as required for
-// IMediaObject::ProcessOutput(). After consuming data provided by
-// ProcessOutput(), call SetLength() to update the buffer availability.
-// Example implementation:
-// http://msdn.microsoft.com/en-us/library/dd376684(v=vs.85).aspx
-class MediaBufferImpl : public IMediaBuffer {
- public:
-  explicit MediaBufferImpl(DWORD max_length)
-      : data_(new BYTE[max_length]), max_length_(max_length) {}
-
-  // IMediaBuffer implementation.
-  STDMETHOD(GetBufferAndLength)(BYTE** buffer, DWORD* length) {
-    if (!buffer || !length)
-      return E_POINTER;
-
-    *buffer = data_.get();
-    *length = length_;
-    return S_OK;
-  }
-
-  STDMETHOD(GetMaxLength)(DWORD* max_length) {
-    if (!max_length)
-      return E_POINTER;
-
-    *max_length = max_length_;
-    return S_OK;
-  }
-
-  STDMETHOD(SetLength)(DWORD length) {
-    if (length > max_length_)
-      return E_INVALIDARG;
-
-    length_ = length;
-    return S_OK;
-  }
-
-  // IUnknown implementation.
-  STDMETHOD_(ULONG, AddRef)() { return InterlockedIncrement(&ref_count_); }
-
-  STDMETHOD(QueryInterface)(REFIID riid, void** object) {
-    if (!object)
-      return E_POINTER;
-    if (riid != IID_IMediaBuffer && riid != IID_IUnknown)
-      return E_NOINTERFACE;
-
-    *object = static_cast<IMediaBuffer*>(this);
-    AddRef();
-    return S_OK;
-  }
-
-  STDMETHOD_(ULONG, Release)() {
-    LONG ref_count = InterlockedDecrement(&ref_count_);
-    if (ref_count == 0)
-      delete this;
-
-    return ref_count;
-  }
-
- private:
-  virtual ~MediaBufferImpl() {}
-
-  std::unique_ptr<BYTE[]> data_;
-  DWORD length_ = 0;
-  const DWORD max_length_;
-  LONG ref_count_ = 0;
-};
-
 }  // namespace
 
 WASAPIAudioInputStream::WASAPIAudioInputStream(
     AudioManagerWin* manager,
     const AudioParameters& params,
     const std::string& device_id,
-    const AudioManager::LogCallback& log_callback,
-    AudioManagerBase::VoiceProcessingMode voice_processing_mode)
-    : manager_(manager),
-      device_id_(device_id),
-      output_device_id_for_aec_(AudioDeviceDescription::kDefaultDeviceId),
-      log_callback_(log_callback),
-      use_voice_processing_(voice_processing_mode ==
-                            AudioManagerBase::VoiceProcessingMode::kEnabled) {
+    const AudioManager::LogCallback& log_callback)
+    : manager_(manager), device_id_(device_id), log_callback_(log_callback) {
   DCHECK(manager_);
   DCHECK(!device_id_.empty());
   DCHECK(!log_callback_.is_null());
 
-  DVLOG_IF(1, use_voice_processing_) << "Using Windows voice capture DSP DMO.";
-
   // Load the Avrt DLL if not already loaded. Required to support MMCSS.
   bool avrt_init = avrt::Initialize();
   DCHECK(avrt_init) << "Failed to load the Avrt.dll";
@@ -255,16 +144,6 @@
     return false;
   }
 
-  // If voice processing is enabled, initialize the DMO that is used for it. The
-  // remainder of the function initializes an audio capture client (the normal
-  // case). Either the DMO or the capture client is used.
-  // TODO(grunell): Refactor out the audio capture client initialization to its
-  // own function.
-  if (use_voice_processing_) {
-    opened_ = InitializeDmo();
-    return opened_;
-  }
-
   // Obtain an IAudioClient interface which enables us to create and initialize
   // an audio stream between an audio application and the audio engine.
   hr = endpoint_device_->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
@@ -312,27 +191,17 @@
   if (started_)
     return;
 
-  // TODO(grunell): Refactor the |use_voice_processing_| conditions in this
-  // function to clean up the code.
-  if (use_voice_processing_) {
-    // Pre-fill render buffer with silence.
-    if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
-            audio_client_for_render_.Get(), audio_render_client_.Get())) {
-      DLOG(WARNING) << "Failed to pre-fill render buffer with silence.";
-    }
-  } else {
-    if (device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId &&
-        system_audio_volume_) {
-      BOOL muted = false;
-      system_audio_volume_->GetMute(&muted);
+  if (device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId &&
+      system_audio_volume_) {
+    BOOL muted = false;
+    system_audio_volume_->GetMute(&muted);
 
-      // If the system audio is muted at the time of capturing, then no need to
-      // mute it again, and later we do not unmute system audio when stopping
-      // capturing.
-      if (!muted) {
-        system_audio_volume_->SetMute(true, NULL);
-        mute_done_ = true;
-      }
+    // If the system audio is muted at the time of capturing, then no need to
+    // mute it again, and later we do not unmute system audio when stopping
+    // capturing.
+    if (!muted) {
+      system_audio_volume_->SetMute(true, NULL);
+      mute_done_ = true;
     }
   }
 
@@ -351,31 +220,21 @@
       base::SimpleThread::Options(base::ThreadPriority::REALTIME_AUDIO)));
   capture_thread_->Start();
 
-  HRESULT hr = E_FAIL;
-  if (use_voice_processing_) {
-    hr = audio_client_for_render_->Start();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to start output streaming: " << std::hex << hr
-                  << ", proceeding without rendering.";
-    }
-  } else {
-    // Start streaming data between the endpoint buffer and the audio engine.
-    hr = audio_client_->Start();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to start input streaming.";
-      log_callback_.Run(base::StringPrintf(
-          "WASAPIAIS::Start: Failed to start audio client, hresult = %#lx",
-          hr));
-    }
+  // Start streaming data between the endpoint buffer and the audio engine.
+  HRESULT hr = audio_client_->Start();
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "Failed to start input streaming.";
+    log_callback_.Run(base::StringPrintf(
+        "WASAPIAIS::Start: Failed to start audio client, hresult = %#lx", hr));
+  }
 
-    if (SUCCEEDED(hr) && audio_render_client_for_loopback_.Get()) {
-      hr = audio_render_client_for_loopback_->Start();
-      if (FAILED(hr))
-        log_callback_.Run(base::StringPrintf(
-            "WASAPIAIS::Start: Failed to start render client for loopback, "
-            "hresult = %#lx",
-            hr));
-    }
+  if (SUCCEEDED(hr) && audio_render_client_for_loopback_.Get()) {
+    hr = audio_render_client_for_loopback_->Start();
+    if (FAILED(hr))
+      log_callback_.Run(base::StringPrintf(
+          "WASAPIAIS::Start: Failed to start render client for loopback, "
+          "hresult = %#lx",
+          hr));
   }
 
   started_ = SUCCEEDED(hr);
@@ -406,21 +265,10 @@
     SetEvent(stop_capture_event_.Get());
   }
 
-  // TODO(grunell): Refactor the |use_voice_processing_| conditions in this
-  // function to clean up the code.
-  if (use_voice_processing_) {
-    // Stop the render audio streaming. The input streaming needs no explicit
-    // stopping.
-    HRESULT hr = audio_client_for_render_->Stop();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to stop output streaming.";
-    }
-  } else {
-    // Stop the input audio streaming.
-    HRESULT hr = audio_client_->Stop();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to stop input streaming.";
-    }
+  // Stop the input audio streaming.
+  HRESULT hr = audio_client_->Stop();
+  if (FAILED(hr)) {
+    LOG(ERROR) << "Failed to stop input streaming.";
   }
 
   // Wait until the thread completes and perform cleanup.
@@ -430,12 +278,6 @@
     capture_thread_.reset();
   }
 
-  if (use_voice_processing_) {
-    HRESULT hr = voice_capture_dmo_->FreeStreamingResources();
-    if (FAILED(hr))
-      DLOG(ERROR) << "Failed to free dmo resources.";
-  }
-
   started_ = false;
   sink_ = NULL;
 }
@@ -524,58 +366,7 @@
 
 void WASAPIAudioInputStream::SetOutputDeviceForAec(
     const std::string& output_device_id) {
-  if (!use_voice_processing_)
-    return;
-
-  if (output_device_id == output_device_id_for_aec_)
-    return;
-
-  output_device_id_for_aec_ = output_device_id;
-
-  if (opened_) {
-    // Set devices.
-    Microsoft::WRL::ComPtr<IPropertyStore> ps;
-    HRESULT hr = voice_capture_dmo_->QueryInterface(IID_IPropertyStore, &ps);
-    if (FAILED(hr) || !ps) {
-      log_callback_.Run(
-          base::StringPrintf("WASAPIAIS:SetOutputDeviceForAec: Getting DMO "
-                             "property store failed."));
-      return;
-    }
-
-    if (!SetDmoDevices(ps.Get())) {
-      log_callback_.Run(
-          "WASAPIAIS:SetOutputDeviceForAec: Setting device indices failed.");
-      return;
-    }
-  }
-
-  if (started_) {
-    DCHECK(opened_);
-    // Recreate the dummy render client on the new output.
-    HRESULT hr = audio_client_for_render_->Stop();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to stop output streaming.";
-    }
-
-    CreateDummyRenderClientsForDmo();
-
-    if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
-            audio_client_for_render_.Get(), audio_render_client_.Get())) {
-      DLOG(WARNING) << "Failed to pre-fill render buffer with silence.";
-    }
-
-    hr = audio_client_for_render_->Start();
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to start output streaming: " << std::hex << hr
-                  << ", proceeding without rendering.";
-    }
-  }
-
-  log_callback_.Run(base::StringPrintf(
-      "WASAPIAIS:SetOutputDeviceForAec: Successfully updated AEC output "
-      "device to %s",
-      output_device_id.c_str()));
+  // Not supported. Do nothing.
 }
 
 void WASAPIAudioInputStream::Run() {
@@ -619,10 +410,31 @@
 
   DVLOG(1) << "AudioBlockFifo buffer count: " << buffers_required;
 
-  bool success =
-      use_voice_processing_ ? RunWithDmo() : RunWithAudioCaptureClient();
+  bool recording = true;
+  bool error = false;
+  HANDLE wait_array[2] = {stop_capture_event_.Get(),
+                          audio_samples_ready_event_.Get()};
 
-  if (!success) {
+  while (recording && !error) {
+    // Wait for a close-down event or a new capture event.
+    DWORD wait_result = WaitForMultipleObjects(2, wait_array, FALSE, INFINITE);
+    switch (wait_result) {
+      case WAIT_OBJECT_0 + 0:
+        // |stop_capture_event_| has been set.
+        recording = false;
+        break;
+      case WAIT_OBJECT_0 + 1:
+        // |audio_samples_ready_event_| has been set.
+        PullCaptureDataAndPushToSink();
+        break;
+      case WAIT_FAILED:
+      default:
+        error = true;
+        break;
+    }
+  }
+
+  if (recording && error) {
     // TODO(henrika): perhaps it worth improving the cleanup here by e.g.
     // stopping the audio client, joining the thread etc.?
     NOTREACHED() << "WASAPI capturing failed with error code "
@@ -637,54 +449,6 @@
   fifo_.reset();
 }
 
-bool WASAPIAudioInputStream::RunWithAudioCaptureClient() {
-  HANDLE wait_array[2] = {stop_capture_event_.Get(),
-                          audio_samples_ready_event_.Get()};
-
-  while (true) {
-    // Wait for a close-down event or a new capture event.
-    DWORD wait_result = WaitForMultipleObjects(2, wait_array, FALSE, INFINITE);
-    switch (wait_result) {
-      case WAIT_OBJECT_0 + 0:
-        // |stop_capture_event_| has been set.
-        return true;
-      case WAIT_OBJECT_0 + 1:
-        // |audio_samples_ready_event_| has been set.
-        PullCaptureDataAndPushToSink();
-        break;
-      case WAIT_FAILED:
-      default:
-        return false;
-    }
-  }
-
-  return false;
-}
-
-bool WASAPIAudioInputStream::RunWithDmo() {
-  while (true) {
-    // Poll every 5 ms, or wake up on capture stop signal.
-    DWORD wait_result = WaitForSingleObject(stop_capture_event_.Get(), 5);
-    switch (wait_result) {
-      case WAIT_OBJECT_0:
-        // |stop_capture_event_| has been set.
-        return true;
-      case WAIT_TIMEOUT:
-        PullDmoCaptureDataAndPushToSink();
-        if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
-                audio_client_for_render_.Get(), audio_render_client_.Get())) {
-          DLOG(WARNING) << "Failed to fill render buffer with silence.";
-        }
-        break;
-      case WAIT_FAILED:
-      default:
-        return false;
-    }
-  }
-
-  return false;
-}
-
 void WASAPIAudioInputStream::PullCaptureDataAndPushToSink() {
   TRACE_EVENT1("audio", "WASAPIAudioInputStream::PullCaptureDataAndPushToSink",
                "sample rate", input_format_.nSamplesPerSec);
@@ -813,106 +577,6 @@
   }  // while (true)
 }
 
-void WASAPIAudioInputStream::PullDmoCaptureDataAndPushToSink() {
-  TRACE_EVENT1("audio",
-               "WASAPIAudioInputStream::PullDmoCaptureDataAndPushToSink",
-               "sample rate", input_format_.nSamplesPerSec);
-
-  // Pull data from the capture endpoint buffer until it's empty or an error
-  // occurs.
-  while (true) {
-    DWORD status = 0;
-    DMO_OUTPUT_DATA_BUFFER data_buffer = {0};
-    data_buffer.pBuffer = media_buffer_.Get();
-
-    // Get processed capture data from the DMO.
-    HRESULT hr =
-        voice_capture_dmo_->ProcessOutput(0,  // dwFlags
-                                          1,  // cOutputBufferCount
-                                          &data_buffer,
-                                          &status);  // Must be ignored.
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "DMO ProcessOutput failed, hr = 0x" << std::hex << hr;
-      break;
-    }
-
-    BYTE* data;
-    ULONG data_length = 0;
-    // Get a pointer to the data buffer. This should be valid until the next
-    // call to ProcessOutput.
-    hr = media_buffer_->GetBufferAndLength(&data, &data_length);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Could not get buffer, hr = 0x" << std::hex << hr;
-      break;
-    }
-
-    if (data_length > 0) {
-      const int samples_produced = data_length / frame_size_bytes_;
-
-      base::TimeTicks capture_time;
-      if (data_buffer.dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIME &&
-          data_buffer.rtTimestamp > 0) {
-        // See conversion notes on |capture_time_100ns| in
-        // PullCaptureDataAndPushToSink().
-        capture_time +=
-            base::TimeDelta::FromMicroseconds(data_buffer.rtTimestamp / 10.0);
-      } else {
-        // We may not get the timestamp from ProcessOutput(), fall back on
-        // current timestamp.
-        capture_time = base::TimeTicks::Now();
-      }
-
-      // Adjust |capture_time| for the FIFO before pushing.
-      capture_time -= AudioTimestampHelper::FramesToTime(
-          fifo_->GetAvailableFrames(), input_format_.nSamplesPerSec);
-
-      fifo_->Push(data, samples_produced, input_format_.wBitsPerSample / 8);
-
-      // Reset length to indicate buffer availability.
-      hr = media_buffer_->SetLength(0);
-      if (FAILED(hr))
-        DLOG(ERROR) << "Could not reset length, hr = 0x" << std::hex << hr;
-
-      // Get a cached AGC volume level which is updated once every second on the
-      // audio manager thread. Note that, |volume| is also updated each time
-      // SetVolume() is called through IPC by the render-side AGC.
-      double volume = 0.0;
-      GetAgcVolume(&volume);
-
-      while (fifo_->available_blocks()) {
-        if (converter_) {
-          if (imperfect_buffer_size_conversion_ &&
-              fifo_->available_blocks() == 1) {
-            // Special case. We need to buffer up more audio before we can
-            // convert or else we'll suffer an underrun.
-            // TODO(grunell): Verify this is really true.
-            break;
-          }
-          converter_->Convert(convert_bus_.get());
-          sink_->OnData(convert_bus_.get(), capture_time, volume);
-
-          // Move the capture time forward for each vended block.
-          capture_time += AudioTimestampHelper::FramesToTime(
-              convert_bus_->frames(), output_format_.nSamplesPerSec);
-        } else {
-          sink_->OnData(fifo_->Consume(), capture_time, volume);
-
-          // Move the capture time forward for each vended block.
-          capture_time += AudioTimestampHelper::FramesToTime(
-              packet_size_frames_, input_format_.nSamplesPerSec);
-        }
-      }
-    }  //  if (data_length > 0)
-
-    if (!(data_buffer.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)) {
-      // The DMO cannot currently produce more data. This is the normal case;
-      // otherwise it means the DMO had more than 10 ms of data available and
-      // ProcessOutput should be called again.
-      break;
-    }
-  }  // while (true)
-}
-
 void WASAPIAudioInputStream::HandleError(HRESULT err) {
   NOTREACHED() << "Error code: " << err;
   if (sink_)
@@ -1334,223 +998,6 @@
       FormatRelatedInitError::kCount);
 }
 
-bool WASAPIAudioInputStream::InitializeDmo() {
-  HRESULT hr = ::CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER,
-                                  IID_IMediaObject, &voice_capture_dmo_);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Creating DMO failed.";
-    return false;
-  }
-
-  if (!SetDmoProperties())
-    return false;
-
-  if (!SetDmoFormat())
-    return false;
-
-  hr = voice_capture_dmo_->AllocateStreamingResources();
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Allocating DMO resources failed.";
-    return false;
-  }
-
-  SetupConverterAndStoreFormatInfo();
-
-  media_buffer_ =
-      new MediaBufferImpl(endpoint_buffer_size_frames_ * frame_size_bytes_);
-
-  if (!CreateDummyRenderClientsForDmo())
-    return false;
-
-  // Get volume interface.
-  Microsoft::WRL::ComPtr<IAudioSessionManager> audio_session_manager;
-  hr = endpoint_device_->Activate(__uuidof(IAudioSessionManager),
-                                  CLSCTX_INPROC_SERVER, NULL,
-                                  &audio_session_manager);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Obtaining audio session manager failed.";
-    return false;
-  }
-  hr = audio_session_manager->GetSimpleAudioVolume(
-      NULL,   // AudioSessionGuid. NULL for default session.
-      FALSE,  // CrossProcessSession.
-      &simple_audio_volume_);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Obtaining audio volume interface failed.";
-    return false;
-  }
-
-  return true;
-}
-
-bool WASAPIAudioInputStream::SetDmoProperties() {
-  Microsoft::WRL::ComPtr<IPropertyStore> ps;
-  HRESULT hr = voice_capture_dmo_->QueryInterface(IID_IPropertyStore, &ps);
-  if (FAILED(hr) || !ps) {
-    DLOG(ERROR) << "Getting DMO property store failed.";
-    return false;
-  }
-
-  // Set devices.
-  if (!SetDmoDevices(ps.Get())) {
-    DLOG(ERROR) << "Setting device indices failed.";
-    return false;
-  }
-
-  // Set DMO mode to AEC only.
-  if (FAILED(CoreAudioUtil::SetVtI4Property(
-          ps.Get(), MFPKEY_WMAAECMA_SYSTEM_MODE, SINGLE_CHANNEL_AEC))) {
-    DLOG(ERROR) << "Setting DMO system mode failed.";
-    return false;
-  }
-
-  // Enable the feature mode. This lets us override the default processing
-  // settings below.
-  if (FAILED(CoreAudioUtil::SetBoolProperty(
-          ps.Get(), MFPKEY_WMAAECMA_FEATURE_MODE, VARIANT_TRUE))) {
-    DLOG(ERROR) << "Setting DMO feature mode failed.";
-    return false;
-  }
-
-  // Disable analog AGC (default enabled).
-  if (FAILED(CoreAudioUtil::SetBoolProperty(
-          ps.Get(), MFPKEY_WMAAECMA_MIC_GAIN_BOUNDER, VARIANT_FALSE))) {
-    DLOG(ERROR) << "Setting DMO mic gain bounder failed.";
-    return false;
-  }
-
-  // Disable noise suppression (default enabled).
-  if (FAILED(CoreAudioUtil::SetVtI4Property(ps.Get(), MFPKEY_WMAAECMA_FEATR_NS,
-                                            0))) {
-    DLOG(ERROR) << "Disabling DMO NS failed.";
-    return false;
-  }
-
-  return true;
-}
-
-bool WASAPIAudioInputStream::SetDmoFormat() {
-  DMO_MEDIA_TYPE mt;  // Media type.
-  mt.majortype = MEDIATYPE_Audio;
-  mt.subtype = MEDIASUBTYPE_PCM;
-  mt.lSampleSize = 0;
-  mt.bFixedSizeSamples = TRUE;
-  mt.bTemporalCompression = FALSE;
-  mt.formattype = FORMAT_WaveFormatEx;
-
-  HRESULT hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Init media type for DMO failed.";
-    return false;
-  }
-
-  WAVEFORMATEX* dmo_output_format =
-      reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat);
-  dmo_output_format->wFormatTag = WAVE_FORMAT_PCM;
-  dmo_output_format->nChannels = 1;
-  dmo_output_format->nSamplesPerSec = 16000;
-  dmo_output_format->nAvgBytesPerSec = 32000;
-  dmo_output_format->nBlockAlign = 2;
-  dmo_output_format->wBitsPerSample = 16;
-  dmo_output_format->cbSize = 0;
-
-  DCHECK(IsSupportedFormatForConversion(*dmo_output_format));
-
-  // Store the format used.
-  input_format_.wFormatTag = dmo_output_format->wFormatTag;
-  input_format_.nChannels = dmo_output_format->nChannels;
-  input_format_.nSamplesPerSec = dmo_output_format->nSamplesPerSec;
-  input_format_.wBitsPerSample = dmo_output_format->wBitsPerSample;
-  input_format_.nBlockAlign = dmo_output_format->nBlockAlign;
-  input_format_.nAvgBytesPerSec = dmo_output_format->nAvgBytesPerSec;
-  input_format_.cbSize = dmo_output_format->cbSize;
-
-  hr = voice_capture_dmo_->SetOutputType(0, &mt, 0);
-  MoFreeMediaType(&mt);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Setting DMO output type failed.";
-    return false;
-  }
-
-  // We use 10 ms buffer size for the DMO.
-  endpoint_buffer_size_frames_ = input_format_.nSamplesPerSec / 100;
-
-  return true;
-}
-
-bool WASAPIAudioInputStream::SetDmoDevices(IPropertyStore* ps) {
-  // Look up the input device's index.
-  const base::Optional<WORD> input_device_index =
-      GetAudioDeviceCollectionIndexFromId(device_id_, eCapture);
-
-  if (!input_device_index) {
-    log_callback_.Run(
-        base::StringPrintf("WASAPIAIS:SetDmoDevices: Could not "
-                           "resolve input device index for %s",
-                           device_id_.c_str()));
-    return false;
-  }
-
-  // Look up the output device's index.
-  const base::Optional<WORD> output_device_index =
-      GetAudioDeviceCollectionIndexFromId(output_device_id_for_aec_, eRender);
-  if (!output_device_index) {
-    log_callback_.Run(
-        base::StringPrintf("WASAPIAIS:SetDmoDevices: Could not "
-                           "resolve output device index for %s",
-                           output_device_id_for_aec_.c_str()));
-    return false;
-  }
-
-  // The DEVICE_INDEXES property packs the input and output indices into the
-  // upper and lower halves of a LONG.
-  LONG device_index_value =
-      (static_cast<ULONG>(*output_device_index) << 16) +
-      (static_cast<ULONG>(*input_device_index) & 0x0000ffff);
-  return !FAILED(CoreAudioUtil::SetVtI4Property(
-      ps, MFPKEY_WMAAECMA_DEVICE_INDEXES, device_index_value));
-}
-
-bool WASAPIAudioInputStream::CreateDummyRenderClientsForDmo() {
-  Microsoft::WRL::ComPtr<IAudioClient> audio_client(CoreAudioUtil::CreateClient(
-      output_device_id_for_aec_, eRender, eConsole));
-  if (!audio_client.Get()) {
-    DLOG(ERROR) << "Failed to create audio client for dummy rendering for DMO.";
-    return false;
-  }
-
-  WAVEFORMATPCMEX mix_format;
-  HRESULT hr =
-      CoreAudioUtil::GetSharedModeMixFormat(audio_client.Get(), &mix_format);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to get mix format.";
-    return false;
-  }
-
-  hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
-                                0,  // Stream flags
-                                0,  // Buffer duration
-                                0,  // Device period
-                                reinterpret_cast<WAVEFORMATEX*>(&mix_format),
-                                NULL);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to initalize audio client for rendering.";
-    return false;
-  }
-
-  Microsoft::WRL::ComPtr<IAudioRenderClient> audio_render_client =
-      CoreAudioUtil::CreateRenderClient(audio_client.Get());
-  if (!audio_render_client.Get()) {
-    DLOG(ERROR) << "Failed to create audio render client.";
-    return false;
-  }
-
-  audio_client_for_render_ = audio_client;
-  audio_render_client_ = audio_render_client;
-
-  return true;
-}
-
 double WASAPIAudioInputStream::ProvideInput(AudioBus* audio_bus,
                                             uint32_t frames_delayed) {
   fifo_->Consume()->CopyTo(audio_bus);
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h
index 938bba7..ee8307d 100644
--- a/media/audio/win/audio_low_latency_input_win.h
+++ b/media/audio/win/audio_low_latency_input_win.h
@@ -58,7 +58,6 @@
 
 #include <Audioclient.h>
 #include <MMDeviceAPI.h>
-#include <dmo.h>
 #include <endpointvolume.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -94,12 +93,10 @@
  public:
   // The ctor takes all the usual parameters, plus |manager| which is the
   // the audio manager who is creating this object.
-  WASAPIAudioInputStream(
-      AudioManagerWin* manager,
-      const AudioParameters& params,
-      const std::string& device_id,
-      const AudioManager::LogCallback& log_callback,
-      AudioManagerBase::VoiceProcessingMode voice_processing_mode);
+  WASAPIAudioInputStream(AudioManagerWin* manager,
+                         const AudioParameters& params,
+                         const std::string& device_id,
+                         const AudioManager::LogCallback& log_callback);
 
   // The dtor is typically called by the AudioManager only and it is usually
   // triggered by calling AudioInputStream::Close().
@@ -119,27 +116,16 @@
   bool started() const { return started_; }
 
  private:
-  // DelegateSimpleThread::Delegate implementation. Calls either
-  // RunWithAudioCaptureClient() or RunWithDmo().
+  // DelegateSimpleThread::Delegate implementation.
   void Run() override;
 
-  // Waits for an event that the audio capture client has data ready.
-  bool RunWithAudioCaptureClient();
-
-  // Polls the DMO (voice processing component) for data every 5 ms.
-  bool RunWithDmo();
-
-  // Pulls capture data from the audio capture client and pushes it to the sink.
+  // Pulls capture data from the endpoint device and pushes it to the sink.
   void PullCaptureDataAndPushToSink();
 
-  // Pulls capture data from the DMO and pushes it to the sink.
-  void PullDmoCaptureDataAndPushToSink();
-
   // Issues the OnError() callback to the |sink_|.
   void HandleError(HRESULT err);
 
-  // The Open() method is divided into these sub methods when not using the
-  // voice processing DMO.
+  // The Open() method is divided into these sub methods.
   HRESULT SetCaptureDevice();
   HRESULT GetAudioEngineStreamFormat();
   // Returns whether the desired format is supported or not and writes the
@@ -155,15 +141,6 @@
   // the format.
   void MaybeReportFormatRelatedInitError(HRESULT hr) const;
 
-  // The Open() method is divided into these sub methods when using the voice
-  // processing DMO. In addition, SetupConverterAndStoreFormatInfo() above is
-  // also called.
-  bool InitializeDmo();
-  bool SetDmoProperties();
-  bool SetDmoFormat();
-  bool SetDmoDevices(IPropertyStore* ps);
-  bool CreateDummyRenderClientsForDmo();
-
   // AudioConverter::InputCallback implementation.
   double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
 
@@ -215,10 +192,10 @@
   WAVEFORMATEX output_format_;
 
   // Contains the audio format we get data from the audio engine in. Set to
-  // |output_format_| at construction and might be changed to a close match
-  // if the audio engine doesn't support the originally set format, or to the
-  // format the voice capture DMO outputs if it's used. Note that this is also
-  // the format after the fifo, i.e. the input format to the converter if any.
+  // |output_format_| at construction and might be changed to a close match if
+  // the audio engine doesn't support the originally set format. Note that this
+  // is also the format after the fifo, i.e. the input format to the converter
+  // if any.
   WAVEFORMATEX input_format_;
 
   bool opened_ = false;
@@ -237,8 +214,7 @@
   // converter.
   size_t packet_size_bytes_ = 0;
 
-  // Length of the audio endpoint buffer, or the buffer size used for the DMO.
-  // That is, the buffer size before the fifo.
+  // Length of the audio endpoint buffer, i.e. the buffer size before the fifo.
   uint32_t endpoint_buffer_size_frames_ = 0;
 
   // Contains the unique name of the selected endpoint device.
@@ -246,10 +222,6 @@
   // device role and is not a valid ID as such.
   std::string device_id_;
 
-  // Contains the unique name of the output device from which to cancel echo, in
-  // case voice processing is enabled, i.e. |use_voice_processing_| is true.
-  std::string output_device_id_for_aec_;
-
   // Pointer to the object that will receive the recorded audio samples.
   AudioInputCallback* sink_ = nullptr;
 
@@ -325,22 +297,6 @@
   UINT64 total_lost_frames_ = 0;
   UINT64 largest_glitch_frames_ = 0;
 
-  // Indicates if the voice processing DMO should be used.
-  bool use_voice_processing_ = false;
-
-  // The voice processing DMO and its data buffer.
-  Microsoft::WRL::ComPtr<IMediaObject> voice_capture_dmo_;
-  Microsoft::WRL::ComPtr<IMediaBuffer> media_buffer_;
-
-  // Dummy rendering when using the DMO. The DMO requires audio rendering to the
-  // device it's set up to use, otherwise it won't produce any capture audio
-  // data. Normally, when the DMO is used there's a render stream, but it's not
-  // guaranteed so we need to support the lack of it. We do this by always
-  // opening a render client and rendering silence to it when the DMO is
-  // running.
-  Microsoft::WRL::ComPtr<IAudioClient> audio_client_for_render_;
-  Microsoft::WRL::ComPtr<IAudioRenderClient> audio_render_client_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(WASAPIAudioInputStream);
diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc
index 170d56e..13cf3734 100644
--- a/media/audio/win/audio_low_latency_input_win_unittest.cc
+++ b/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -170,16 +170,11 @@
 // also allows the user to modify the default settings.
 class AudioInputStreamWrapper {
  public:
-  explicit AudioInputStreamWrapper(AudioManager* audio_manager,
-                                   bool use_voice_processing)
+  explicit AudioInputStreamWrapper(AudioManager* audio_manager)
       : audio_man_(audio_manager) {
     EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
         AudioDeviceDescription::kDefaultDeviceId, false, &default_params_)));
     EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
-    if (use_voice_processing) {
-      default_params_.set_effects(default_params_.effects() |
-                                  AudioParameters::ECHO_CANCELLER);
-    }
     frames_per_buffer_ = default_params_.frames_per_buffer();
   }
 
@@ -227,9 +222,8 @@
 
 // Convenience method which creates a default AudioInputStream object.
 static AudioInputStream* CreateDefaultAudioInputStream(
-    AudioManager* audio_manager,
-    bool use_voice_processing) {
-  AudioInputStreamWrapper aisw(audio_manager, use_voice_processing);
+    AudioManager* audio_manager) {
+  AudioInputStreamWrapper aisw(audio_manager);
   AudioInputStream* ais = aisw.Create();
   return ais;
 }
@@ -264,9 +258,7 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
 };
 
-// The test class. The boolean parameter specifies if voice processing should be
-// used.
-class WinAudioInputTest : public ::testing::TestWithParam<bool> {
+class WinAudioInputTest : public ::testing::Test {
  public:
   WinAudioInputTest() {
     audio_manager_ =
@@ -311,16 +303,15 @@
   media::AudioDeviceDescriptions device_descriptions;
   device_info_accessor.GetAudioInputDeviceDescriptions(&device_descriptions);
 
-  // All devices in the device description list should have the experimental
-  // echo canceller capability.
+  // No device should have any effects.
   for (const auto& device : device_descriptions) {
     AudioParameters params =
         device_info_accessor.GetInputStreamParameters(device.unique_id);
-    EXPECT_EQ(params.effects(), AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+    EXPECT_EQ(params.effects(), AudioParameters::NO_EFFECTS);
   }
 
   // The two loopback devices are not included in the device description list
-  // above. They should have no effects.
+  // above. They should also have no effects.
   AudioParameters params = device_info_accessor.GetInputStreamParameters(
       AudioDeviceDescription::kLoopbackInputDeviceId);
   EXPECT_EQ(params.effects(), AudioParameters::NO_EFFECTS);
@@ -331,27 +322,27 @@
 }
 
 // Test Create(), Close() calling sequence.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
   ScopedAudioInputStream ais(
-      CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
+      CreateDefaultAudioInputStream(audio_manager_.get()));
   ais.Close();
 }
 
 // Test Open(), Close() calling sequence.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
   ScopedAudioInputStream ais(
-      CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
+      CreateDefaultAudioInputStream(audio_manager_.get()));
   EXPECT_TRUE(ais->Open());
   ais.Close();
 }
 
 // Test Open(), Start(), Close() calling sequence.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
   ScopedAudioInputStream ais(
-      CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
+      CreateDefaultAudioInputStream(audio_manager_.get()));
   EXPECT_TRUE(ais->Open());
   MockAudioInputCallback sink;
   ais->Start(&sink);
@@ -359,10 +350,10 @@
 }
 
 // Test Open(), Start(), Stop(), Close() calling sequence.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
   ScopedAudioInputStream ais(
-      CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
+      CreateDefaultAudioInputStream(audio_manager_.get()));
   EXPECT_TRUE(ais->Open());
   MockAudioInputCallback sink;
   ais->Start(&sink);
@@ -371,10 +362,10 @@
 }
 
 // Test some additional calling sequences.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
   ScopedAudioInputStream ais(
-      CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
+      CreateDefaultAudioInputStream(audio_manager_.get()));
 
   // Open(), Open() should fail the second time.
   EXPECT_TRUE(ais->Open());
@@ -396,7 +387,7 @@
   ais.Close();
 }
 
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
 
   int count = 0;
@@ -405,7 +396,7 @@
 
   // Create default WASAPI input stream which records in stereo using
   // the shared mixing rate. The default buffer size is 10ms.
-  AudioInputStreamWrapper aisw(audio_manager_.get(), GetParam());
+  AudioInputStreamWrapper aisw(audio_manager_.get());
   ScopedAudioInputStream ais(aisw.Create());
   EXPECT_TRUE(ais->Open());
 
@@ -479,7 +470,7 @@
 }
 
 // Test that we can capture a stream in loopback.
-TEST_P(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
+TEST_F(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
   AudioDeviceInfoAccessorForTests device_info_accessor(audio_manager_.get());
   ABORT_AUDIO_TEST_IF_NOT(device_info_accessor.HasAudioOutputDevices() &&
                           CoreAudioUtil::IsSupported());
@@ -513,7 +504,7 @@
 // To include disabled tests in test execution, just invoke the test program
 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
 // environment variable to a value greater than 0.
-TEST_P(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
+TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
 
   // Name of the output PCM file containing captured data. The output file
@@ -521,7 +512,7 @@
   // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
   const char* file_name = "out_10sec.pcm";
 
-  AudioInputStreamWrapper aisw(audio_manager_.get(), GetParam());
+  AudioInputStreamWrapper aisw(audio_manager_.get());
   ScopedAudioInputStream ais(aisw.Create());
   ASSERT_TRUE(ais->Open());
 
@@ -535,7 +526,7 @@
   ais.Close();
 }
 
-TEST_P(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
+TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
   ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
 
   // This is basically the same test as WASAPIAudioInputStreamRecordToFile
@@ -570,8 +561,6 @@
     // Otherwise (e.g. 44.1kHz, 22.05kHz etc) we convert to 48kHz.
     const int hw_sample_rate = params.sample_rate();
     params.Reset(params.format(), test.layout, test.rate, test.frames);
-    if (GetParam())
-      params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
 
     std::string file_name(base::StringPrintf(
         "resampled_10sec_%i_to_%i_%s.pcm", hw_sample_rate, params.sample_rate(),
@@ -595,8 +584,4 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(/* Intentially left empty */,
-                        WinAudioInputTest,
-                        ::testing::Bool());
-
 }  // namespace media
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index 0dd2120f..0d98248 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -23,7 +23,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/win/windows_version.h"
 #include "media/audio/audio_device_description.h"
-#include "media/audio/audio_features.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/win/audio_device_listener_win.h"
 #include "media/audio/win/audio_low_latency_input_win.h"
@@ -187,12 +186,6 @@
   if (user_buffer_size)
     parameters.set_frames_per_buffer(user_buffer_size);
 
-  if (device_id != AudioDeviceDescription::kLoopbackInputDeviceId &&
-      device_id != AudioDeviceDescription::kLoopbackWithMuteDeviceId) {
-    parameters.set_effects(parameters.effects() |
-                           AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
-  }
-
   return parameters;
 }
 
@@ -263,14 +256,7 @@
     const LogCallback& log_callback) {
   // Used for both AUDIO_PCM_LOW_LATENCY and AUDIO_PCM_LINEAR.
   DVLOG(1) << "MakeLowLatencyInputStream: " << device_id;
-
-  VoiceProcessingMode voice_processing_mode =
-      params.effects() & AudioParameters::ECHO_CANCELLER
-          ? VoiceProcessingMode::kEnabled
-          : VoiceProcessingMode::kDisabled;
-
-  return new WASAPIAudioInputStream(this, params, device_id, log_callback,
-                                    voice_processing_mode);
+  return new WASAPIAudioInputStream(this, params, device_id, log_callback);
 }
 
 std::string AudioManagerWin::GetDefaultInputDeviceID() {
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index b9c47d9a..fc681c8ac 100644
--- a/media/audio/win/core_audio_util_win.cc
+++ b/media/audio/win/core_audio_util_win.cc
@@ -902,71 +902,4 @@
   return true;
 }
 
-HRESULT CoreAudioUtil::GetDeviceCollectionIndex(const std::string& device_id,
-                                                EDataFlow data_flow,
-                                                WORD* index) {
-  ComPtr<IMMDeviceEnumerator> enumerator = CreateDeviceEnumerator();
-  if (!enumerator.Get()) {
-    DLOG(ERROR) << "Failed to create device enumerator.";
-    return E_FAIL;
-  }
-
-  ComPtr<IMMDeviceCollection> device_collection;
-  HRESULT hr = enumerator->EnumAudioEndpoints(data_flow, DEVICE_STATE_ACTIVE,
-                                              &device_collection);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to get device collection.";
-    return hr;
-  }
-
-  UINT number_of_devices = 0;
-  hr = device_collection->GetCount(&number_of_devices);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to get device collection count.";
-    return hr;
-  }
-
-  ComPtr<IMMDevice> device;
-  for (WORD i = 0; i < number_of_devices; ++i) {
-    hr = device_collection->Item(i, &device);
-    if (FAILED(hr)) {
-      DLOG(WARNING) << "Failed to get device.";
-      continue;
-    }
-    ScopedCoMem<WCHAR> current_device_id;
-    hr = device->GetId(&current_device_id);
-    if (FAILED(hr)) {
-      DLOG(WARNING) << "Failed to get device id.";
-      continue;
-    }
-    if (base::UTF16ToUTF8(current_device_id.get()) == device_id) {
-      *index = i;
-      return S_OK;
-    }
-  }
-
-  DVLOG(1) << "No matching device found.";
-  return S_FALSE;
-}
-
-HRESULT CoreAudioUtil::SetBoolProperty(IPropertyStore* property_store,
-                                       REFPROPERTYKEY key,
-                                       VARIANT_BOOL value) {
-  base::win::ScopedPropVariant pv;
-  PROPVARIANT* pv_ptr = pv.Receive();
-  pv_ptr->vt = VT_BOOL;
-  pv_ptr->boolVal = value;
-  return property_store->SetValue(key, pv.get());
-}
-
-HRESULT CoreAudioUtil::SetVtI4Property(IPropertyStore* property_store,
-                                       REFPROPERTYKEY key,
-                                       LONG value) {
-  base::win::ScopedPropVariant pv;
-  PROPVARIANT* pv_ptr = pv.Receive();
-  pv_ptr->vt = VT_I4;
-  pv_ptr->lVal = value;
-  return property_store->SetValue(key, pv.get());
-}
-
 }  // namespace media
diff --git a/media/audio/win/core_audio_util_win.h b/media/audio/win/core_audio_util_win.h
index 6a2b0dab..8a3174b 100644
--- a/media/audio/win/core_audio_util_win.h
+++ b/media/audio/win/core_audio_util_win.h
@@ -207,23 +207,6 @@
   static bool FillRenderEndpointBufferWithSilence(
       IAudioClient* client, IAudioRenderClient* render_client);
 
-  // Gets the device collection index for the device specified by |device_id|.
-  // If the device is found in the device collection, the index is written to
-  // |*index| and S_OK is returned. If the device is not found, S_FALSE is
-  // returned and |*index| is left unchanged. In case of an error, the error
-  // result is returned and |*index| is left unchanged.
-  static HRESULT GetDeviceCollectionIndex(const std::string& device_id,
-                                          EDataFlow data_flow,
-                                          WORD* index);
-
-  // Sets the property identified by |key| to |value| in |*property_store|.
-  static HRESULT SetBoolProperty(IPropertyStore* property_store,
-                                 REFPROPERTYKEY key,
-                                 VARIANT_BOOL value);
-  static HRESULT SetVtI4Property(IPropertyStore* property_store,
-                                 REFPROPERTYKEY key,
-                                 LONG value);
-
  private:
   CoreAudioUtil() {}
   ~CoreAudioUtil() {}
diff --git a/media/filters/android/video_frame_extractor.cc b/media/filters/android/video_frame_extractor.cc
index cdbe3cc..dc1c299 100644
--- a/media/filters/android/video_frame_extractor.cc
+++ b/media/filters/android/video_frame_extractor.cc
@@ -76,6 +76,11 @@
   }
 
   auto packet = ReadVideoFrame();
+  if (!packet) {
+    OnError();
+    return;
+  }
+
   ConvertPacket(packet.get());
   NotifyComplete(
       std::vector<uint8_t>(packet->data, packet->data + packet->size),
diff --git a/media/filters/android/video_frame_extractor_unittest.cc b/media/filters/android/video_frame_extractor_unittest.cc
index 549ebb6..91a9bc5 100644
--- a/media/filters/android/video_frame_extractor_unittest.cc
+++ b/media/filters/android/video_frame_extractor_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -41,36 +43,55 @@
   ~VideoFrameExtractorTest() override {}
 
  protected:
-  void SetUp() override {
-    data_source_ = std::make_unique<FileDataSource>();
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
 
-    CHECK(data_source_->Initialize(GetTestDataFilePath("bear.mp4")));
+  ExtractVideoFrameResult ExtractFrame(const base::FilePath& file_path) {
+    base::File file(
+        file_path, base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
+    DCHECK(file.IsValid());
+    data_source_ = std::make_unique<FileDataSource>();
+    CHECK(data_source_->Initialize(file_path));
     extractor_ = std::make_unique<VideoFrameExtractor>(data_source_.get());
+
+    ExtractVideoFrameResult result;
+    base::RunLoop loop;
+    extractor_->Start(
+        base::BindOnce(&OnFrameExtracted, &result, loop.QuitClosure()));
+    loop.Run();
+    return result;
   }
 
-  void TearDown() override { extractor_.reset(); }
-
-  VideoFrameExtractor* extractor() { return extractor_.get(); }
+  const base::FilePath& temp_dir() const { return temp_dir_.GetPath(); }
 
  private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::ScopedTempDir temp_dir_;
   std::unique_ptr<FileDataSource> data_source_;
   std::unique_ptr<VideoFrameExtractor> extractor_;
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   DISALLOW_COPY_AND_ASSIGN(VideoFrameExtractorTest);
 };
 
-// Verifies the encoded video frame is extracted correctly.
+// Verifies the encoded video frame can be extracted correctly.
 TEST_F(VideoFrameExtractorTest, ExtractVideoFrame) {
-  ExtractVideoFrameResult result;
-  base::RunLoop loop;
-  extractor()->Start(
-      base::BindOnce(&OnFrameExtracted, &result, loop.QuitClosure()));
-  loop.Run();
+  auto result = ExtractFrame(GetTestDataFilePath("bear.mp4"));
   EXPECT_TRUE(result.success);
   EXPECT_GT(result.encoded_frame.size(), 0u);
   EXPECT_EQ(result.decoder_config.codec(), VideoCodec::kCodecH264);
 }
 
+// Verifies graceful failure when trying to extract frame from an invalid video
+// file.
+TEST_F(VideoFrameExtractorTest, ExtractInvalidVideoFile) {
+  // Creates a dummy video file, frame extraction should fail.
+  base::FilePath file = temp_dir().AppendASCII("test.txt");
+  EXPECT_GT(base::WriteFile(file, "123", sizeof("123")), 0);
+
+  auto result = ExtractFrame(file);
+  EXPECT_FALSE(result.success);
+  EXPECT_EQ(result.encoded_frame.size(), 0u);
+  EXPECT_FALSE(result.decoder_config.IsValidConfig());
+}
+
 }  // namespace
 }  // namespace media
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index e4931a6..b7b56a5e 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -334,7 +334,11 @@
   DCHECK(network_loader_);
   DCHECK(forwarding_client_);
   DCHECK(!is_waiting_follow_redirect_call_);
-  HandleComplete(status);
+
+  URLLoaderCompletionStatus modified_status(status);
+  if (status.error_code == net::OK)
+    modified_status.cors_preflight_timing_info.swap(preflight_timing_info_);
+  HandleComplete(modified_status);
 }
 
 void CORSURLLoader::StartRequest() {
@@ -384,7 +388,7 @@
   // preflight request when |fetch_cors_flag_| is false (e.g., when the origin
   // of the url is equal to the origin of the request.
   if (!fetch_cors_flag_ || !NeedsPreflight(request_)) {
-    StartNetworkRequest(net::OK, base::nullopt);
+    StartNetworkRequest(net::OK, base::nullopt, base::nullopt);
     return;
   }
 
@@ -402,14 +406,18 @@
 
 void CORSURLLoader::StartNetworkRequest(
     int error_code,
-    base::Optional<CORSErrorStatus> status) {
+    base::Optional<CORSErrorStatus> status,
+    base::Optional<PreflightTimingInfo> preflight_timing_info) {
   if (error_code != net::OK) {
     HandleComplete(status ? URLLoaderCompletionStatus(*status)
                           : URLLoaderCompletionStatus(error_code));
     return;
   }
-
   DCHECK(!status);
+
+  if (preflight_timing_info)
+    preflight_timing_info_.push_back(*preflight_timing_info);
+
   mojom::URLLoaderClientPtr network_client;
   network_client_binding_.Bind(mojo::MakeRequest(&network_client));
   // Binding |this| as an unretained pointer is safe because
diff --git a/services/network/cors/cors_url_loader.h b/services/network/cors/cors_url_loader.h
index 2c9e3a4..5d67f84 100644
--- a/services/network/cors/cors_url_loader.h
+++ b/services/network/cors/cors_url_loader.h
@@ -10,6 +10,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "url/gurl.h"
@@ -80,8 +81,10 @@
 
  private:
   void StartRequest();
-  void StartNetworkRequest(int net_error,
-                           base::Optional<CORSErrorStatus> status);
+  void StartNetworkRequest(
+      int net_error,
+      base::Optional<CORSErrorStatus> status,
+      base::Optional<PreflightTimingInfo> preflight_timing_info);
 
   // Called when there is a connection error on the upstream pipe used for the
   // actual request.
@@ -151,6 +154,9 @@
   // We need to save this for redirect.
   net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
 
+  // Holds timing info if a preflight was made.
+  std::vector<PreflightTimingInfo> preflight_timing_info_;
+
   // Outlives |this|.
   const OriginAccessList* const origin_access_list_;
 
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 6f6aad7..3771204 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -11,6 +11,7 @@
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/cors/cors.h"
@@ -271,7 +272,8 @@
 
     std::move(completion_callback_)
         .Run(net::ERR_FAILED,
-             CORSErrorStatus(mojom::CORSError::kPreflightDisallowedRedirect));
+             CORSErrorStatus(mojom::CORSError::kPreflightDisallowedRedirect),
+             base::nullopt);
 
     RemoveFromController();
     // |this| is deleted here.
@@ -281,6 +283,14 @@
                             const ResourceResponseHead& head) {
     FinalizeLoader();
 
+    timing_info_.start_time = head.request_start;
+    timing_info_.finish_time = base::TimeTicks::Now();
+    timing_info_.alpn_negotiated_protocol = head.alpn_negotiated_protocol;
+    timing_info_.connection_info = head.connection_info;
+    head.headers->GetNormalizedHeader("Timing-Allow-Origin",
+                                      &timing_info_.timing_allow_origin);
+    timing_info_.transfer_size = head.encoded_data_length;
+
     base::Optional<CORSErrorStatus> detected_error_status;
     std::unique_ptr<PreflightResult> result = CreatePreflightResult(
         final_url, head, original_request_, tainted_, &detected_error_status);
@@ -299,9 +309,12 @@
                                  original_request_.url, std::move(result));
     }
 
+    base::Optional<PreflightTimingInfo> timing_info;
+    if (!detected_error_status)
+      timing_info = std::move(timing_info_);
     std::move(completion_callback_)
         .Run(detected_error_status ? net::ERR_FAILED : net::OK,
-             detected_error_status);
+             detected_error_status, std::move(timing_info));
 
     RemoveFromController();
     // |this| is deleted here.
@@ -315,7 +328,7 @@
     const int error = loader_->NetError();
     DCHECK_NE(error, net::OK);
     FinalizeLoader();
-    std::move(completion_callback_).Run(error, base::nullopt);
+    std::move(completion_callback_).Run(error, base::nullopt, base::nullopt);
     RemoveFromController();
     // |this| is deleted here.
   }
@@ -337,6 +350,8 @@
   // Holds SimpleURLLoader instance for the CORS-preflight request.
   std::unique_ptr<SimpleURLLoader> loader_;
 
+  PreflightTimingInfo timing_info_;
+
   // Holds caller's information.
   PreflightController::CompletionCallback completion_callback_;
   const ResourceRequest original_request_;
@@ -384,7 +399,7 @@
           request.request_initiator->Serialize(), request.url,
           request.fetch_credentials_mode, request.method, request.headers,
           request.is_revalidating)) {
-    std::move(callback).Run(net::OK, base::nullopt);
+    std::move(callback).Run(net::OK, base::nullopt, base::nullopt);
     return;
   }
 
diff --git a/services/network/cors/preflight_controller.h b/services/network/cors/preflight_controller.h
index 9aaa178d..779c8f11 100644
--- a/services/network/cors/preflight_controller.h
+++ b/services/network/cors/preflight_controller.h
@@ -17,6 +17,7 @@
 #include "services/network/public/cpp/cors/cors_error_status.h"
 #include "services/network/public/cpp/cors/preflight_cache.h"
 #include "services/network/public/cpp/cors/preflight_result.h"
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -32,8 +33,11 @@
 // See also https://crbug.com/803766 to check a design doc.
 class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final {
  public:
+  // PreflightTimingInfo is provided only when a preflight request was made.
   using CompletionCallback =
-      base::OnceCallback<void(int, base::Optional<CORSErrorStatus>)>;
+      base::OnceCallback<void(int net_error,
+                              base::Optional<CORSErrorStatus>,
+                              base::Optional<PreflightTimingInfo>)>;
   // Creates a CORS-preflight ResourceRequest for a specified |request| for a
   // URL that is originally requested.
   static std::unique_ptr<ResourceRequest> CreatePreflightRequestForTesting(
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index e2f2e85..a300222 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -18,6 +18,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/network_service.h"
 #include "services/network/public/cpp/cors/cors.h"
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -199,8 +200,10 @@
   }
 
  protected:
-  void HandleRequestCompletion(int net_error,
-                               base::Optional<CORSErrorStatus> status) {
+  void HandleRequestCompletion(
+      int net_error,
+      base::Optional<CORSErrorStatus> status,
+      base::Optional<PreflightTimingInfo> timing_info) {
     net_error_ = net_error;
     status_ = status;
     run_loop_->Quit();
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index 0a6c0ed..5086f0b 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -84,6 +84,8 @@
   sources = [
     "cors/cors_error_status.cc",
     "cors/cors_error_status.h",
+    "cors/preflight_timing_info.cc",
+    "cors/preflight_timing_info.h",
     "data_element.cc",
     "data_element.h",
     "http_raw_request_response_info.cc",
diff --git a/services/network/public/cpp/cors/preflight_timing_info.cc b/services/network/public/cpp/cors/preflight_timing_info.cc
new file mode 100644
index 0000000..2691e8c9
--- /dev/null
+++ b/services/network/public/cpp/cors/preflight_timing_info.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
+
+namespace network {
+
+namespace cors {
+
+PreflightTimingInfo::PreflightTimingInfo() = default;
+PreflightTimingInfo::PreflightTimingInfo(const PreflightTimingInfo& info) =
+    default;
+PreflightTimingInfo::~PreflightTimingInfo() = default;
+
+bool PreflightTimingInfo::operator==(const PreflightTimingInfo& rhs) const {
+  return start_time == rhs.start_time && finish_time == rhs.finish_time &&
+         alpn_negotiated_protocol == rhs.alpn_negotiated_protocol &&
+         connection_info == rhs.connection_info &&
+         timing_allow_origin == rhs.timing_allow_origin &&
+         transfer_size == rhs.transfer_size;
+}
+
+}  // namespace cors
+
+}  // namespace network
diff --git a/services/network/public/cpp/cors/preflight_timing_info.h b/services/network/public/cpp/cors/preflight_timing_info.h
new file mode 100644
index 0000000..a59c56f9
--- /dev/null
+++ b/services/network/public/cpp/cors/preflight_timing_info.h
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_CORS_PREFLIGHT_TIMING_INFO_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_CORS_PREFLIGHT_TIMING_INFO_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "base/memory/scoped_refptr.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "services/network/public/mojom/cors.mojom-shared.h"
+
+namespace network {
+
+namespace cors {
+
+// Stores performance monitoring information for CORS preflight requests that
+// are made in the NetworkService. Will be used to carry information from the
+// NetworkService to call sites via URLLoaderCompletionStatus.
+struct COMPONENT_EXPORT(NETWORK_CPP_BASE) PreflightTimingInfo {
+  PreflightTimingInfo();
+  PreflightTimingInfo(const PreflightTimingInfo& info);
+  ~PreflightTimingInfo();
+
+  base::TimeTicks start_time;
+  base::TimeTicks finish_time;
+  std::string alpn_negotiated_protocol;
+  net::HttpResponseInfo::ConnectionInfo connection_info =
+      net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN;
+  std::string timing_allow_origin;
+  uint64_t transfer_size = 0;
+
+  bool operator==(const PreflightTimingInfo& rhs) const;
+};
+
+}  // namespace cors
+
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_CORS_PREFLIGHT_TIMING_INFO_H_
diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h
index 5b97007d..9dc08eaabb 100644
--- a/services/network/public/cpp/network_ipc_param_traits.h
+++ b/services/network/public/cpp/network_ipc_param_traits.h
@@ -112,11 +112,21 @@
   IPC_STRUCT_TRAITS_MEMBER(failed_parameter)
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(network::cors::PreflightTimingInfo)
+  IPC_STRUCT_TRAITS_MEMBER(start_time)
+  IPC_STRUCT_TRAITS_MEMBER(finish_time)
+  IPC_STRUCT_TRAITS_MEMBER(alpn_negotiated_protocol)
+  IPC_STRUCT_TRAITS_MEMBER(connection_info)
+  IPC_STRUCT_TRAITS_MEMBER(timing_allow_origin)
+  IPC_STRUCT_TRAITS_MEMBER(transfer_size)
+IPC_STRUCT_TRAITS_END()
+
 IPC_STRUCT_TRAITS_BEGIN(network::URLLoaderCompletionStatus)
   IPC_STRUCT_TRAITS_MEMBER(error_code)
   IPC_STRUCT_TRAITS_MEMBER(extended_error_code)
   IPC_STRUCT_TRAITS_MEMBER(exists_in_cache)
   IPC_STRUCT_TRAITS_MEMBER(completion_time)
+  IPC_STRUCT_TRAITS_MEMBER(cors_preflight_timing_info)
   IPC_STRUCT_TRAITS_MEMBER(encoded_data_length)
   IPC_STRUCT_TRAITS_MEMBER(encoded_body_length)
   IPC_STRUCT_TRAITS_MEMBER(decoded_body_length)
diff --git a/services/network/public/cpp/url_loader_completion_status.cc b/services/network/public/cpp/url_loader_completion_status.cc
index 32004ff..4a6d60d 100644
--- a/services/network/public/cpp/url_loader_completion_status.cc
+++ b/services/network/public/cpp/url_loader_completion_status.cc
@@ -29,6 +29,7 @@
          extended_error_code == rhs.extended_error_code &&
          exists_in_cache == rhs.exists_in_cache &&
          completion_time == rhs.completion_time &&
+         cors_preflight_timing_info == rhs.cors_preflight_timing_info &&
          encoded_data_length == rhs.encoded_data_length &&
          encoded_body_length == rhs.encoded_body_length &&
          decoded_body_length == rhs.decoded_body_length &&
diff --git a/services/network/public/cpp/url_loader_completion_status.h b/services/network/public/cpp/url_loader_completion_status.h
index 3897f29..6e243cb 100644
--- a/services/network/public/cpp/url_loader_completion_status.h
+++ b/services/network/public/cpp/url_loader_completion_status.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "net/ssl/ssl_info.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "services/network/public/mojom/cors.mojom-shared.h"
 
 namespace network {
@@ -48,6 +49,9 @@
   // Time the request completed.
   base::TimeTicks completion_time;
 
+  // Timing info if CORS preflights were made.
+  std::vector<cors::PreflightTimingInfo> cors_preflight_timing_info;
+
   // Total amount of data received from the network.
   int64_t encoded_data_length = 0;
 
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 3a18479..684cd62 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -168,6 +168,8 @@
 #define SK_SUPPORT_LEGACY_AAA_CHOICE
 #endif
 
+#define SK_LEGACY_XFORM_CANVAS_IN_PICTURE_IMAGES
+
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
index 303028a..0eac070 100644
--- a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
@@ -5,11 +5,19 @@
 # These tests currently fail when run with --enable-features=NetworkService
 
 # Uncategorized timeouts or test failures.
+
+# https://crbug.com/882650 (NS OOP)
 -*__multiprocess_mode
+
+# https://crbug.com/893561
 -org.chromium.android_webview.test.AcceptLanguageTest.testAcceptLanguage
+
+# https://crbug.com/893562
 -org.chromium.android_webview.test.AwContentsClientAutoLoginTest.testAutoLoginOnGoogleCom
 -org.chromium.android_webview.test.AwContentsClientAutoLoginTest.testAutoLoginOnNonGoogle
 -org.chromium.android_webview.test.AwContentsClientAutoLoginTest.testAutoLoginWithNullAccount
+
+# https://crbug.com/893563
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testFullscreenNotSupported_video
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testFullscreenNotSupported_videoInsideDiv
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testOnShowCustomViewAndPlayWithHtmlControl_video
@@ -18,7 +26,11 @@
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsEnabledDuringFullscreenPlayback_video
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsEnabledDuringFullscreenPlayback_videoInsideDiv
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsTransferredToEmbedded
+
+# https://crbug.com/893564
 -org.chromium.android_webview.test.AwContentsClientGetDefaultVideoPosterTest.testGetDefaultVideoPoster
+
+# https://crbug.com/893566
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testBaseUrlOfLoadDataSentInRefererHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledForExistingFiles
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledForIframe
@@ -55,15 +67,23 @@
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNullInputStreamCausesErrorForMainFrame
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testOnLoadResourceCalledWithCorrectUrl
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testOnReceivedErrorCallback
+
+# https://crbug.com/893567
 -org.chromium.android_webview.test.AwContentsClientShouldOverrideUrlLoadingTest.testCalledForIframeUnsupportedSchemeNavigations
 -org.chromium.android_webview.test.AwContentsClientShouldOverrideUrlLoadingTest.testWindowOpenCustomSchemeUrlInPopup
 -org.chromium.android_webview.test.AwContentsClientShouldOverrideUrlLoadingTest.testWindowOpenHttpUrlInPopup
 -org.chromium.android_webview.test.AwContentsClientShouldOverrideUrlLoadingTest.testWindowOpenHttpUrlInPopupAddsTrailingSlash
+
+# https://crbug.com/893568
 -org.chromium.android_webview.test.AwContentsTest.testCanInjectHeaders
 -org.chromium.android_webview.test.AwContentsTest.testDownload
 -org.chromium.android_webview.test.AwContentsTest.testEscapingOfErrorPage
+
+# https://crbug.com/893569
 -org.chromium.android_webview.test.AwServiceWorkerClientTest.testFetchResourceLoadingError
 -org.chromium.android_webview.test.AwServiceWorkerClientTest.testInvokeInterceptCallback
+
+# https://crbug.com/893570
 -org.chromium.android_webview.test.AwSettingsTest.testAssetUrl
 -org.chromium.android_webview.test.AwSettingsTest.testBlockNetworkLoadsWithAudio
 -org.chromium.android_webview.test.AwSettingsTest.testBlockNetworkLoadsWithHttpResources
@@ -76,7 +96,11 @@
 -org.chromium.android_webview.test.AwSettingsTest.testFileUrlAccessToggleDoesNotBlockResourceUrls
 -org.chromium.android_webview.test.AwSettingsTest.testFileUrlAccessWithTwoViews
 -org.chromium.android_webview.test.AwSettingsTest.testResourceUrl
+
+# https://crbug.com/893572
 -org.chromium.android_webview.test.ClientOnPageStartedTest.testOnPageStartedCalledOnceOnError
+
+# https://crbug.com/893573
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testIframeSubresource
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testImageSubresource
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testMainFrame
@@ -87,18 +111,26 @@
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testOnNonExistentResourceUrl
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testUserGesture
 -org.chromium.android_webview.test.ClientOnReceivedError2Test.testUserGestureForIframeSubresource
+
+# https://crbug.com/893574
 -org.chromium.android_webview.test.ClientOnReceivedErrorTest.testCacheMiss
 -org.chromium.android_webview.test.ClientOnReceivedErrorTest.testNonExistentAssetUrl
 -org.chromium.android_webview.test.ClientOnReceivedErrorTest.testNonExistentResourceUrl
 -org.chromium.android_webview.test.ClientOnReceivedErrorTest.testOnReceivedErrorOnInvalidScheme
 -org.chromium.android_webview.test.ClientOnReceivedErrorTest.testOnReceivedErrorOnInvalidUrl
+
+# https://crbug.com/891722
 -org.chromium.android_webview.test.ClientOnReceivedHttpErrorTest.testAfterRedirect
 -org.chromium.android_webview.test.ClientOnReceivedHttpErrorTest.testForMainResource
 -org.chromium.android_webview.test.ClientOnReceivedHttpErrorTest.testForSubresource
 -org.chromium.android_webview.test.ClientOnReceivedHttpErrorTest.testForUserGesture
 -org.chromium.android_webview.test.ClientOnReceivedHttpErrorTest.testNotCalledIfNoHttpError
+
+# https://crbug.com/893575
 -org.chromium.android_webview.test.CookieManagerStartupTest.testShouldInterceptRequestDeadlock
 -org.chromium.android_webview.test.CookieManagerStartupTest.testStartup
+
+# https://crbug.com/893576
 -org.chromium.android_webview.test.CookieManagerTest.testAcceptCookie
 -org.chromium.android_webview.test.CookieManagerTest.testAcceptFileSchemeCookies
 -org.chromium.android_webview.test.CookieManagerTest.testRejectFileSchemeCookies
@@ -106,27 +138,44 @@
 -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookieForWebSocketEnabledCase
 -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookiesArePerWebview
 -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyJavascriptCookie
+
+# https://crbug.com/893577
 -org.chromium.android_webview.test.HttpCacheTest.testHttpCacheIsInsideCacheDir
+
+# https://crbug.com/893579
 -org.chromium.android_webview.test.KeySystemTest.testNotSupportFooKeySystem
 -org.chromium.android_webview.test.KeySystemTest.testSupportClearKeySystem
 -org.chromium.android_webview.test.KeySystemTest.testSupportPlatformKeySystemNoPrefix
+
+# https://crbug.com/893580
 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testLoadDataWithBaseUrlAccessingFile
 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testSetCookieInIframe
 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testThirdPartyCookieInIframe
+
+# https://crbug.com/893581
 -org.chromium.android_webview.test.LoadUrlTest.testLoadUrlWithExtraHeaders
 -org.chromium.android_webview.test.LoadUrlTest.testRedirectAndReloadWithExtraHeaders
 -org.chromium.android_webview.test.LoadUrlTest.testReloadWithExtraHeaders
 -org.chromium.android_webview.test.LoadUrlTest.testRendererNavigationAndGoBackWithExtraHeaders
+
+# https://crbug.com/893582
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingHardcodedMalwareUrl
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingHardcodedPhishingUrl
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingOnSafeBrowsingHitBackToSafety
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingShowsNetworkErrorForInvisibleViews
+
+# https://crbug.com/893583
 -org.chromium.android_webview.test.VisualStateTest.testOnPageCommitVisible
 -org.chromium.android_webview.test.VisualStateTest.testVisualStateCallbackFromJsDuringFullscreenTransitions
 -org.chromium.android_webview.test.VisualStateTest.testVisualStateCallbackWaitsForJs
 -org.chromium.android_webview.test.VisualStateTest.testVisualStateCallbackWhenContainerViewDetached
+
+# https://crbug.com/893584
 -org.chromium.android_webview.test.WebKitHitTestTest.testSrcEmailType
 -org.chromium.android_webview.test.WebKitHitTestTest.testSrcGeoType
 -org.chromium.android_webview.test.WebKitHitTestTest.testSrcPhoneType
+
+# https://crbug.com/893585
 -org.chromium.android_webview.test.WebViewWebVrTest.testWebVrNotFunctional
+
diff --git a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter
index f5e9158f..53d6a30 100644
--- a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter
+++ b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter
@@ -25,10 +25,6 @@
 CrSettingsAnimatedPagesTest.All
 CrSettingsFocusRowBehavior.FocusTest
 CrSettingsSyncPageTest.All
-LoginUITest.PRE_InterruptedAutoStartEnrollment
-LoginUITest.InterruptedAutoStartEnrollment
-LoginUITest.PRE_LoginNoExceptions
-LoginUITest.LoginNoExceptions
 MaterialBookmarksFocusTest.All
 MaterialHistoryFocusTest.All
 PrintPreviewDestinationDialogInteractiveTest.*
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 944357d..7fddb11 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2415,21 +2415,6 @@
             ]
         }
     ],
-    "MacV2Sandbox": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MacV2Sandbox"
-                    ]
-                }
-            ]
-        }
-    ],
     "MediaFoundationH264Encoding": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 05f7c4de8..8acbab2 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -115,9 +115,6 @@
 Bug(none) compositing/3d-cube.html [ Failure ]
 Bug(none) compositing/absolute-inside-out-of-view-fixed.html [ Failure ]
 Bug(none) compositing/animation/hidden-composited.html [ Failure ]
-Bug(none) compositing/backface-visibility/backface-visibility-image.html [ Failure ]
-Bug(none) compositing/backface-visibility/backface-visibility-webgl.html [ Failure ]
-Bug(none) compositing/canvas-with-object-fit-contain-in-composited-layer.html [ Failure ]
 Bug(none) compositing/change-preferCompositingToLCDText-setting.html [ Failure ]
 Bug(none) compositing/color-matching/image-color-matching.html [ Failure ]
 Bug(none) compositing/columns/composited-in-paginated.html [ Failure Crash ]
@@ -137,7 +134,6 @@
 Bug(none) compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow.html [ Failure Crash ]
 Bug(none) compositing/filters/sw-shadow-overlaps-hw-layer.html [ Failure Crash ]
 Bug(none) compositing/filters/sw-shadow-overlaps-hw-shadow.html [ Failure Crash ]
-Bug(none) compositing/fixed-background-after-style-recalc.html [ Failure ]
 Bug(none) compositing/fixed-body-background-positioned.html [ Failure ]
 Bug(none) compositing/fixed-position-changed-to-absolute.html [ Failure ]
 Bug(none) compositing/flat-with-transformed-child.html [ Failure ]
@@ -322,21 +318,13 @@
 Bug(none) compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
 Bug(none) compositing/overlap-blending/reflection-opacity-huge.html [ Failure Crash ]
 Bug(none) compositing/perpendicular-layer-sorting.html [ Failure ]
-Bug(none) compositing/plugins/webplugin-reflection.html [ Failure Crash ]
-Bug(none) compositing/reflections/animation-inside-reflection.html [ Failure Crash ]
-Bug(none) compositing/reflections/compositing-change-inside-reflection.html [ Failure Crash ]
 Bug(none) compositing/reflections/deeply-nested-reflections.html [ Failure Crash ]
 Bug(none) compositing/reflections/load-video-in-reflection.html [ Failure Crash ]
 Bug(none) compositing/reflections/nested-reflection-anchor-point.html [ Failure Crash ]
 Bug(none) compositing/reflections/nested-reflection-animated.html [ Failure Crash ]
 Bug(none) compositing/reflections/nested-reflection-mask-change.html [ Failure Crash ]
-Bug(none) compositing/reflections/nested-reflection-on-overflow.html [ Failure Crash ]
 Bug(none) compositing/reflections/nested-reflection-opacity.html [ Failure Crash ]
-Bug(none) compositing/reflections/nested-reflection-size-change.html [ Failure Crash ]
-Bug(none) compositing/reflections/nested-reflection-transition.html [ Failure Crash ]
-Bug(none) compositing/reflections/nested-reflection.html [ Failure Crash ]
 Bug(none) compositing/reflections/reflection-in-composited.html [ Failure Crash ]
-Bug(none) compositing/reflections/transform-inside-reflection.html [ Failure Crash ]
 Bug(none) compositing/rtl/rtl-absolute-overflow-scrolled.html [ Failure Crash ]
 Bug(none) compositing/rtl/rtl-absolute-overflow.html [ Failure Crash ]
 Bug(none) compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure Crash ]
@@ -389,13 +377,12 @@
 Bug(none) compositing/video/video-controls-layer-creation-squashing.html [ Crash Failure ]
 Bug(none) compositing/video/video-controls-layer-creation.html [ Crash Failure ]
 Bug(none) compositing/video/video-poster.html [ Crash Failure ]
-Bug(none) compositing/video/video-reflection.html [ Crash Failure ]
 Bug(none) compositing/visibility/layer-visible-content.html [ Failure ]
 Bug(none) compositing/visibility/overlays.html [ Failure Crash ]
 Bug(none) compositing/visibility/visibility-image-layers-dynamic.html [ Crash Failure ]
 Bug(none) compositing/visibility/visibility-image-layers.html [ Failure ]
 Bug(none) compositing/visibility/visibility-simple-video-layer.html [ Failure ]
-Bug(none) compositing/webgl/webgl-reflection.html [ Failure Crash ]
+Bug(none) compositing/webgl/webgl-reflection.html [ Failure Pass ]
 Bug(none) compositing/webgl/webgl-with-accelerated-background-color.html [ Failure Pass ]
 Bug(none) fast/backgrounds/background-inherit-color-bug.html [ Failure ]
 Bug(none) fast/backgrounds/background-leakage-transforms.html [ Failure ]
@@ -429,7 +416,6 @@
 Bug(none) fast/box-shadow/basic-shadows.html [ Failure ]
 Bug(none) fast/box-sizing/box-sizing.html [ Failure ]
 Bug(none) fast/canvas/canvas-composite-video.html [ Failure ]
-Bug(none) fast/canvas/imagebitmap/transferFromImageBitmap-no-alpha.html [ Failure ]
 Bug(none) fast/canvas/OffscreenCanvas-2d-drawImage-in-worker.html [ Failure ]
 Bug(none) fast/canvas/OffscreenCanvas-2d-drawImage.html [ Failure ]
 Bug(none) fast/canvas/OffscreenCanvas-2d-gradients-in-worker.html [ Failure ]
@@ -482,18 +468,17 @@
 Bug(none) fast/events/touch/compositor-touch-hit-rects-global.html [ Failure Crash ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-many.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-non-composited-scroll.html [ Failure ]
+Bug(none) fast/events/touch/compositor-touch-hit-rects-non-composited-scroll-overflow-with-handler.html  [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-scroll.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-squashing.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-trigger-commit.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-transform-changed-nolayout.html [ Failure ]
+Bug(none) fast/events/touch/compositor-touch-hit-rects-with-negative-child.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 Bug(none) fast/events/touch/gesture/gesture-scroll-by-page.html [ Failure ]
 Bug(none) fast/events/touch/touch-handler-assert-input-range.html [ Failure ]
 Bug(none) fast/events/touch/touch-rect-assert-first-layer-special.html [ Failure ]
 Bug(none) fast/events/touch/touch-rect-crash-on-unpromote-layer.html [ Failure ]
-Bug(none) fast/events/wheel/wheelevent-in-horizontal-scrollbar-in-rtl.html [ Failure ]
-Bug(none) fast/events/wheel/wheelevent-in-vertical-scrollbar-in-rtl.html [ Failure ]
-Bug(none) fast/events/wheel/wheelevent-in-scrolling-div.html [ Failure ]
 Bug(none) fast/forms/button-default-title.html [ Failure ]
 Bug(none) fast/forms/fieldset/fieldset-align.html [ Failure ]
 Bug(none) fast/forms/select/listbox-appearance-basic.html [ Failure ]
@@ -535,11 +520,8 @@
 Bug(none) fast/multicol/composited-relpos-resize.html [ Failure ]
 Bug(none) fast/multicol/composited-with-child-layer-in-next-column.html [ Failure ]
 Bug(none) fast/multicol/composited-with-overflow-in-next-column.html [ Failure ]
-Bug(none) fast/multicol/fixedpos-in-transform-at-column-boundary.html [ Crash ]
 Bug(none) fast/multicol/insane-column-count-and-padding-nested-crash.html [ Crash ]
 Bug(none) fast/multicol/mixed-opacity-test.html [ Failure ]
-Bug(none) fast/multicol/nested-and-unbreakable-crash.html [ Crash ]
-Bug(none) fast/multicol/overflow-unsplittable.html [ Failure ]
 Bug(none) fast/multicol/span/invalid-spanner-in-transform.html [ Failure ]
 Bug(none) fast/multicol/vertical-lr/composited-relpos-overlapping-will-change.html [ Failure ]
 Bug(none) fast/multicol/vertical-rl/composited-relpos-overlapping-will-change.html [ Failure ]
@@ -553,7 +535,6 @@
 Bug(none) fast/selectors/166.html [ Failure ]
 Bug(none) fast/sub-pixel/repaint-subpixel-layer-in-subpixel-composited-layer.html [ Failure ]
 Bug(none) fast/sub-pixel/should-not-repaint-subpixel-composited-layer.html [ Failure ]
-Bug(none) fast/sub-pixel/sub-pixel-transparency-layer.html [ Failure ]
 Bug(none) fast/sub-pixel/transformed-iframe-copy-on-scroll.html [ Failure ]
 Bug(none) fast/table/023.html [ Failure ]
 Bug(none) fast/table/027-vertical.html [ Failure ]
@@ -602,7 +583,6 @@
 Bug(none) fast/table/backgr_simple-table-row.html [ Failure ]
 Bug(none) fast/table/backgr_simple-table.html [ Failure ]
 Bug(none) fast/text/capitalize-boundaries.html [ Failure ]
-Bug(none) fast/text/selection/emphasis.html [ Failure ]
 Bug(none) fast/text/font-stretch-variant.html [ Failure ]
 Bug(none) fast/text/font-weight-variant.html [ Failure ]
 Bug(none) fast/text/international/bidi-neutral-run.html [ Failure ]
@@ -615,8 +595,6 @@
 Bug(none) images/cross-fade-invalidation.html [ Crash Failure Pass ]
 Bug(none) paint/pagination/pagination-change-clip-crash.html [ Failure ]
 
-Bug(none) fast/multicol/input-with-overflow-second-column.html [ Failure ]
-
 # Less invalidations or different invalidations without pixel failures.
 # Some might be good. Some might be under-invalidations for which under-invalidation
 # checking failed.
@@ -665,7 +643,6 @@
 Bug(none) paint/invalidation/scroll/invalidate-after-composited-scroll-of-window.html [ Failure ]
 Bug(none) paint/invalidation/invalidation-after-opacity-change-subtree.html [ Failure ]
 Bug(none) paint/invalidation/position/layout-state-only-positioned.html [ Failure ]
-Bug(none) paint/invalidation/overflow/opacity-change-on-overflow-float.html [ Failure ]
 Bug(none) paint/invalidation/position/relative-positioned-movement-repaint.html [ Failure ]
 Bug(none) paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ]
 Bug(none) paint/invalidation/scroll/scroll-fixed-layer-with-transformed-parent-layer.html [ Failure ]
@@ -720,12 +697,10 @@
 
 # Other extra layers.
 Bug(none) compositing/composite-scrollable-fixed-position-when-descendants-composite.html [ Failure ]
-Bug(none) paint/invalidation/position/abspos-shift-image-incorrect-repaint.html [ Failure ]
 Bug(none) paint/invalidation/image/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
 Bug(none) paint/invalidation/overflow/clipped-overflow-visible-subtree.html [ Failure ]
 Bug(none) paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-scrolling-contents-outline-change.html [ Failure ]
-Bug(none) paint/invalidation/create-layer-repaint.html [ Failure ]
 Bug(none) paint/invalidation/filters/filter-on-html-element-with-fixed-position-child.html [ Failure ]
 Bug(none) paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed.html [ Failure Crash ]
 Bug(none) paint/invalidation/scroll/inline-style-change-in-scrolled-view.html [ Failure ]
@@ -851,19 +826,14 @@
 Bug(none) fast/events/platform-wheelevent-paging-y-in-scrolling-div.html [ Failure ]
 Bug(none) fast/events/scroll-in-scaled-page-with-overflow-hidden.html [ Failure Crash ]
 Bug(none) fast/events/scrollbar-double-click.html [ Failure ]
-Bug(none) fast/events/wheel/wheelevent-basic.html [ Failure ]
 Bug(none) fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html [ Failure ]
 Bug(none) fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html [ Failure ]
 Bug(none) fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html [ Failure ]
 Bug(none) fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html [ Failure ]
 Bug(none) fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html [ Failure ]
 
-Bug(none) fast/scrolling/absolute-position-behind-scrollbar.html [ Failure ]
-Bug(none) fast/scrolling/fixed-position-behind-scrollbar.html [ Failure Timeout ]
 Bug(none) fast/scroll-snap/snaps-after-scrollbar-scrolling.html [ Failure ]
 Bug(none) fast/events/remove-child-onscroll.html [ Timeout ]
-Bug(none) fast/events/mouse-wheel-main-frame-scroll.html [ Timeout ]
-Bug(none) fast/scrolling/hover-during-scroll.html [ Timeout ]
 
 crbug.com/651292 svg/dynamic-updates/SVGImageElement-dom-x-attr.html [ Pass Failure ]
 crbug.com/651292 svg/dynamic-updates/SVGImageElement-svgdom-height-prop.html [ Pass Failure ]
@@ -876,7 +846,6 @@
 crbug.com/651292 svg/dynamic-updates/SVGImageElement-dom-preserveAspectRatio-attr.html [ Pass Failure ]
 crbug.com/651292 svg/dynamic-updates/SVGImageElement-dom-y-attr.html [ Pass Failure ]
 
-Bug(none) fast/scrolling/overflow-scrollability.html [ Failure ]
 Bug(none) compositing/lots-of-img-layers-with-opacity.html [ Failure ]
 Bug(none) compositing/lots-of-img-layers.html [ Failure ]
 Bug(none) compositing/opacity-with-mask.html [ Failure ]
@@ -966,13 +935,11 @@
 crbug.com/589265 compositing/masks/masked-ancestor.html [ Failure ]
 crbug.com/589265 compositing/masks/multiple-masks.html [ Failure ]
 crbug.com/589265 compositing/masks/simple-composited-mask.html [ Failure ]
-crbug.com/589265 compositing/nested-border-radius-composited-child.html [ Failure ]
 crbug.com/589265 compositing/overflow/border-radius-on-parent-composited-grandchild.html [ Failure ]
 crbug.com/589265 compositing/overflow/nested-border-radius-clipping.html [ Failure ]
 crbug.com/589265 fast/borders/border-radius-with-composited-child.html [ Failure ]
 crbug.com/589265 fast/clip/overflow-border-radius-composited-parent.html [ Failure ]
 crbug.com/589265 fast/clip/overflow-border-radius-composited.html [ Failure ]
-crbug.com/589265 paint/overflow/composited-rounded-clip-floating-element.html [ Failure ]
 crbug.com/589265 svg/custom/mask-changes.svg [ Failure ]
 crbug.com/589265 svg/custom/mask-colorspace.svg [ Failure ]
 
@@ -995,9 +962,7 @@
 crbug.com/589265 compositing/perspective-interest-rect.html [ Failure ]
 
 # Some work remains to fully support composited animation and scrolling.
-crbug.com/702350 transitions/opacity-transform-transitions-inside-iframe.html [ Timeout ]
 crbug.com/702350 virtual/threaded/transitions/opacity-transform-transitions-inside-iframe.html [ Timeout ]
-crbug.com/702353 transitions/transition-end-event-destroy-iframe.html [ Timeout ]
 crbug.com/702353 virtual/threaded/transitions/transition-end-event-destroy-iframe.html [ Timeout ]
 crbug.com/702365 virtual/threaded/animations/composited-filter-webkit-filter.html [ Failure ]
 crbug.com/702370 virtual/threaded/animations/compositor-independent-transform-cancel.html [ Failure ]
@@ -1008,10 +973,6 @@
 # approaches. http://crbug.com/692310#c12
 crbug.com/692310 virtual/threaded/animations/composited-animations-rotate-zero-degrees.html [ Pass Failure Timeout ]
 
-# This test uses internal call to initiate smooth scrolling on impl-thread.
-crbug.com/667946 fast/compositor-wheel-scroll-latching/animated-scroll/touchpad-scroll-impl-to-main.html [ Failure ]
-
-
 # virtual/threaded variants of sub-directories and tests already skipped or marked as failing above.
 Bug(none) virtual/threaded/http/tests/devtools/ [ Skip ]
 Bug(none) virtual/threaded/fast/events/pinch/gesture-pinch-zoom-scroll-bubble.html [ Failure Crash ]
@@ -1094,7 +1055,6 @@
 crbug.com/738613 paint/invalidation/compositing/text-match-highlight.html [ Failure ]
 crbug.com/738613 paint/invalidation/compositing/updating-scrolling-container-and-content.html [ Failure ]
 crbug.com/738613 paint/invalidation/compositing/updating-scrolling-container.html [ Failure ]
-crbug.com/738613 paint/invalidation/table/dynamic-table-vertical-alignment-change.html [ Failure ]
 crbug.com/738613 paint/invalidation/line-flow-with-floats-1.html [ Failure ]
 crbug.com/738613 paint/invalidation/line-flow-with-floats-10.html [ Failure ]
 crbug.com/738613 paint/invalidation/line-flow-with-floats-2.html [ Failure ]
@@ -1107,8 +1067,6 @@
 crbug.com/738613 paint/invalidation/line-flow-with-floats-9.html [ Failure ]
 crbug.com/738613 paint/invalidation/requestAnimation-translation-leave-traces.html [ Failure ]
 crbug.com/738613 paint/invalidation/overflow/resize-child-within-overflow.html [ Failure ]
-crbug.com/738613 paint/invalidation/position/shift-relative-positioned-container-with-image-addition.html [ Failure ]
-crbug.com/738613 paint/invalidation/position/shift-relative-positioned-container-with-image-removal.html [ Failure ]
 crbug.com/738613 paint/invalidation/table/table-shrink-row-repaint.html [ Failure ]
 crbug.com/738613 paint/invalidation/position/transform-absolute-in-positioned-container.html [ Failure ]
 crbug.com/738613 paint/invalidation/table/scroll-relative-table-inside-table-cell.html [ Failure ]
@@ -1191,10 +1149,7 @@
 Bug(none) paint/invalidation/scroll/overflow-scroll-delete.html [ Failure ]
 Bug(none) paint/invalidation/overflow/vertical-overflow-child.html [ Failure ]
 Bug(none) fast/forms/textarea/textarea-scrolled-mask.html [ Failure ]
-Bug(none) fast/css/sticky/sticky-both-sides-top-left-constrained.html [ Failure ]
 Bug(none) fast/css/sticky/sticky-vertically-overconstrained.html [ Failure ]
-Bug(none) fast/css/sticky/sticky-overflowing.html [ Failure ]
-Bug(none) fast/sub-pixel/save-layer-bounds-should-snap.html [ Failure ]
 
 # Different layer tree about scrolling layer. Some may also have wrong invalidations.
 crbug.com/738613 compositing/iframes/connect-compositing-iframe-delayed.html [ Failure ]
@@ -1219,14 +1174,6 @@
 crbug.com/738613 paint/invalidation/scroll/repaint-composited-child-in-scrolled-container.html [ Failure ]
 crbug.com/738613 paint/invalidation/scroll/scroll-stacking-context-backface-visiblity-leaves-traces.html [ Failure ]
 
-# Sticky positioned objects are not invalidated when scroll offset changes.
-crbug.com/762098 compositing/overflow/composited-nested-sticky-table.html [ Failure ]
-crbug.com/762098 compositing/overflow/mixed-composited-nested-sticky-overflow-scroller.html [ Failure ]
-crbug.com/762098 compositing/overflow/composited-nested-sticky-left.html [ Failure ]
-crbug.com/762098 compositing/overflow/composited-nested-sticky-deep.html [ Failure ]
-crbug.com/762098 compositing/overflow/composited-nested-sticky-top.html [ Failure ]
-crbug.com/762098 compositing/overflow/non-composited-sticky-element-in-main-thread-scrolled-composited-ancestor.html [ Failure ]
-
 # Subsequence under-invalidation of scroller related to ScrollHitTest.
 crbug.com/738613 compositing/overflow/overflow-scroll-with-pointer-events-toggle.html [ Failure Crash ]
 # Null pointer in PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer().
@@ -1242,47 +1189,13 @@
 Bug(none) compositing/rendering-contexts.html [ Failure ]
 Bug(none) compositing/overflow-trumps-transform-style.html [ Failure ]
 
-crbug.com/796768 fast/css/getComputedStyle/getComputedStyle-zoom-and-background-size.html [ Crash ]
-crbug.com/796768 fast/multicol/composited-layer-will-change.html [ Failure ]
-crbug.com/796768 fast/multicol/hit-test-gap-between-pages-flipped.html [ Crash ]
-crbug.com/796768 fast/multicol/hit-test-gap-between-pages.html [ Crash ]
-crbug.com/796768 fast/multicol/multicol-becomes-paged-fixed-height.html [ Crash ]
-crbug.com/796768 fast/multicol/nested-with-composited-and-multicol-crash.html [ Crash ]
-crbug.com/796768 fast/multicol/scrollable-basic.html [ Failure Crash ]
-crbug.com/796768 fast/multicol/scrollbar-taller-than-content-box-freeze.html [ Crash ]
-crbug.com/796768 fast/multicol/scrolling-column-rules.html [ Crash ]
-crbug.com/796768 fast/overflow/hidden-html-paged-body.html [ Crash ]
-crbug.com/796768 fast/overflow/scroll-html-paged-body.html [ Crash ]
-crbug.com/796768 fast/pagination/auto-height-with-break.html [ Crash ]
-crbug.com/796768 fast/pagination/break-in-paged-overflow.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-x-rtl-vertical-rl.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-x-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-x-vertical-rl.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-x.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-y-rtl-vertical-rl.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-y-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/caret-range-outside-paged-y.html [ Crash ]
-crbug.com/796768 fast/pagination/div-make-paginated.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-horizontal-tb-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-horizontal-tb-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-vertical-lr-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-vertical-lr-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-vertical-rl-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-x-vertical-rl-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-horizontal-tb-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-horizontal-tb-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-vertical-lr-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-vertical-lr-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-vertical-rl-ltr.html [ Crash ]
-crbug.com/796768 fast/pagination/div-y-vertical-rl-rtl.html [ Crash ]
-crbug.com/796768 fast/pagination/multicol.html [ Crash ]
-crbug.com/796768 fast/pagination/repeating-thead-tfoot-paged-x.html [ Failure ]
-crbug.com/796768 fast/pagination/repeating-thead-tfoot-paged-y.html [ Failure ]
-crbug.com/796768 fast/pagination/short-pages-tall-content.html [ Crash ]
-crbug.com/796768 fast/text-autosizing/constrained-and-overflow-paged-x-ancestor.html [ Crash ]
-crbug.com/796768 paint/clipath/change-mask-clip-path-multicol-crash.html [ Crash ]
-crbug.com/796768 paint/invalidation/overflow/paged-with-overflowing-block-rl.html [ Failure ]
-crbug.com/796768 paint/pagination/composited-paginated-outlined-box.html [ Failure ]
+Bug(none) fast/multicol/composited-layer-will-change.html [ Failure ]
+Bug(none) fast/pagination/multicol.html [ Crash ]
+Bug(none) fast/pagination/repeating-thead-tfoot-paged-x.html [ Failure ]
+Bug(none) fast/pagination/repeating-thead-tfoot-paged-y.html [ Failure ]
+Bug(none) paint/clipath/change-mask-clip-path-multicol-crash.html [ Crash ]
+Bug(none) paint/invalidation/overflow/paged-with-overflowing-block-rl.html [ Failure ]
+Bug(none) paint/pagination/composited-paginated-outlined-box.html [ Failure ]
 
 Bug(none) css3/blending/mix-blend-mode-simple.html [ Pass Timeout ]
 Bug(none) css3/blending/mix-blend-mode-simple-text.html [ Pass Timeout ]
@@ -1297,7 +1210,6 @@
 crbug.com/882075 compositing/overflow/composited-sticky-element-with-non-integer-relative-position-to-container.html [ Failure ]
 
 # Failures accumulated during the bot was borrowed by spv175.
-Bug(none) compositing/columns/geometry-map-paginated-assert.html [ Crash ]
 Bug(none) compositing/geometry/fixed-position-composited-page-scale-scroll.html [ Crash ]
 Bug(none) compositing/geometry/fixed-position-composited-page-scale-smaller-than-viewport.html [ Crash ]
 Bug(none) compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html [ Crash ]
@@ -1332,23 +1244,6 @@
 Bug(none) fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Crash ]
 Bug(none) fast/frames/iframe-double-scale-contents.html [ Crash ]
 Bug(none) fast/history/scroll-restoration/scroll-restoration-scale-not-impacted.html [ Crash ]
-Bug(none) fast/pagination/body-make-paginated.html [ Crash ]
-Bug(none) fast/pagination/html-make-paginated.html [ Crash ]
-Bug(none) fast/pagination/paged-x-column-gap.html [ Crash ]
-Bug(none) fast/pagination/paged-x-to-paged-y.html [ Crash ]
-Bug(none) fast/pagination/paged-y-to-paged-x.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-horizontal-tb-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-horizontal-tb-rtl.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-vertical-lr-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-vertical-lr-rtl.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-vertical-rl-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-x-vertical-rl-rtl.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-horizontal-tb-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-horizontal-tb-rtl.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-vertical-lr-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-vertical-lr-rtl.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-vertical-rl-ltr.html [ Crash ]
-Bug(none) fast/pagination/viewport-y-vertical-rl-rtl.html [ Crash ]
 Bug(none) fast/scrolling/editor-command-scroll-page-scale.html [ Crash ]
 Bug(none) fast/scrolling/keyboard-scroll-page-scale.html [ Crash ]
 Bug(none) fast/scrolling/same-page-navigate.html [ Crash ]
@@ -1357,14 +1252,9 @@
 Bug(none) images/server-side-imagemap.html [ Crash ]
 Bug(none) paint/invalidation/position/fixed-right-in-page-scale.html [ Crash ]
 Bug(none) svg/as-image/image-respects-pageScaleFactor-change.html [ Crash ]
-Bug(none) compositing/fixed-background-composited-html.html [ Failure ]
-Bug(none) compositing/fixed-background-negative-z-index-fixed.html [ Failure ]
-Bug(none) compositing/layer-creation/iframe-background-attachment-fixed.html [ Failure ]
 Bug(none) compositing/overflow/ancestor-with-clip-path.html [ Failure ]
 Bug(none) compositing/overflow/descendant-with-clip-path.html [ Failure ]
-Bug(none) compositing/overflow/mixed-composited-nested-sticky.html [ Failure ]
 Bug(none) fast/css/pseudo-element-selector-scrollbar-hover.html [ Failure ]
-Bug(none) fast/css/sticky/sticky-clip-rel-child.html [ Failure ]
 Bug(none) fast/scrolling/custom-scrollbar-style-applied-with-overflow-attribute.html [ Failure ]
 Bug(none) paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
 Bug(none) paint/invalidation/subpixel-shadow-included-in-invalidation.html [ Failure ]
@@ -1376,12 +1266,8 @@
 Bug(none) svg/transforms/text-with-mask-with-svg-transform.svg [ Failure ]
 
 crbug.com/833083 paint/invalidation/compositing/composited-layers-move-in-subpixels.html [ Failure ]
-crbug.com/837467 paint/invalidation/compositing/end-of-opacity-transition.html [ Failure ]
-crbug.com/857322 paint/invalidation/background/obscured-background-no-repaint.html [ Failure Crash ]
 
 Bug(none) compositing/squashing/frame-clip-squashed-scrolled.html [ Failure ]
-Bug(none) fast/block/positioning/vertical-rl/002.html [ Failure ]
-Bug(none) rootscroller/gesture-scroll-document-not-root-scroller.html [ Failure ]
 
 Bug(none) fast/scroll-snap/snap-scrolls-visual-viewport.html [ Crash ]
 Bug(none) paint/invalidation/vertical-align1.html [ Failure ]
@@ -1390,11 +1276,6 @@
 Bug(none) virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Failure ]
 Bug(none) virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Failure ]
 
-Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-into-center.html [ Pass Failure Crash ]
-Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-pan-position-fixed.html [ Pass Failure Crash ]
-Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-pan-within-zoomed-viewport.html [ Pass Failure Crash ]
-Bug(none) compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Failure ]
-
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
@@ -1409,7 +1290,6 @@
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-text.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg-foreign-object.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-foreign-object.html [ Failure ]
-Bug(none) paint/float/float-under-inline-self-painting-change.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-image.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg-image.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-img-element.html [ Failure ]
@@ -1418,4 +1298,14 @@
 Bug(none) fast/events/touch/compositor-touch-hit-rects-svg-root.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects-table.html [ Failure ]
 Bug(none) virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-table.html [ Failure ]
+
+Bug(none) virtual/threaded/fast/events/pinch/gesture-pinch-fake-mouse-wheel.html [ Pass Failure Crash ]
+Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-into-center.html [ Pass Failure Crash ]
+Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-pan-position-fixed.html [ Pass Failure Crash ]
+Bug(none) virtual/threaded/fast/events/pinch/pinch-zoom-pan-within-zoomed-viewport.html [ Pass Failure Crash ]
+Bug(none) compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Failure ]
+
+Bug(none) paint/float/float-under-inline-self-painting-change.html [ Failure ]
 Bug(none) compositing/overflow/overlap-testing-ancestor-scroller-high-dpi.html [ Failure ]
+Bug(none) virtual/outofblink-cors-ns/http/tests/navigation/same-document-scroll-position-restore.html [ Timeout ]
+Bug(none) virtual/threaded/external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index d96a0413..5a5de79 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1770,6 +1770,8 @@
 crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/legend-after-margin-with-before-border-horizontal-mode.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/legend-small-after-margin-before-border-horizontal-mode.html [ Failure ]
 
+crbug.com/894457 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/dynamic-bsize-change.html [ Crash ]
+
 # ====== LayoutNG-only failures until here ======
 
 ### sheriff 2018-05-28
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
index 2bfe0312..67f0b1e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window-expected.txt
@@ -6,6 +6,7 @@
 FAIL Requests with PUT method require CORS Preflight and succeed. assert_equals: expected "backgroundfetchsuccess" but got "backgroundfetchfail"
 FAIL Requests with text/json content type require CORS Preflight and succeed. assert_equals: expected "backgroundfetchsuccess" but got "backgroundfetchfail"
 PASS Using Background Fetch to successfully fetch a single resource
+PASS Registration object gets updated values when a background fetch completes.
 PASS Background Fetch that exceeds the quota throws a QuotaExceededError
 FAIL Fetches can have requests with duplicate URLs promise_test: Unhandled rejection with value: object "TypeError: Fetches with duplicate requests are not yet supported. Consider adding query params to make the requests unique. For updates check http://crbug.com/871174"
 FAIL Fetches can have requests with a body promise_test: Unhandled rejection with value: object "TypeError: Requests with a body are not yet supported. For updates check http://crbug.com/774054"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
index 7729718..9ed7e94 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
@@ -147,6 +147,24 @@
 
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registrationId = uniqueId();
+  const registration =
+    await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
+
+  assert_equals(registration.result, '');
+  assert_equals(registration.failureReason, '');
+
+  const {type, eventRegistration, results} =
+    await getMessageFromServiceWorker();
+  assert_equals('backgroundfetchsuccess', type);
+
+  assert_equals(eventRegistration.id, registration.id);
+  assert_equals(registration.result, 'success');
+  assert_equals(registration.failureReason, '');
+
+}, 'Registration object gets updated values when a background fetch completes.');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registrationId = uniqueId();
 
   // Very large download total that will definitely exceed the quota.
   const options = {downloadTotal: Number.MAX_SAFE_INTEGER};
@@ -240,6 +258,9 @@
   assert_equals(eventRegistration.result, 'failure');
   assert_equals(eventRegistration.failureReason, 'bad-status');
 
+  assert_equals(registration.result, 'failure');
+  assert_equals(registration.failureReason, 'bad-status');
+
 }, 'Using Background Fetch to fetch a non-existent resource should fail.');
 
 backgroundFetchTest(async (test, backgroundFetch) => {
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
index 7a06cfac..d79ea788 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
@@ -291,10 +291,6 @@
 }, 'createImageBitmap in linear RGB from a transparent SRGB HTMLImageElement \
 (BMP, ICO, PNG, WEBP) with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more test scenarios for loading the image element from P3
-// and Rec2020 images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // SVG Image - SRGB
@@ -308,10 +304,6 @@
     }).then(testImageBitmapFromSVG);
 }, 'createImageBitmap in linear RGB from a SRGB SVG image with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the image element from P3 and Rec2020
-// SVG images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLVideoElement - SRGB
@@ -325,10 +317,6 @@
     }).then(testImageBitmapVideoSource);
 }, 'createImageBitmap in linear RGB from a SRGB HTMLVideoElement with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the video element from P3 and Rec2020
-// videos.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLCanvasElement - Opaque SRGB
@@ -383,7 +371,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Blob - Opaque SRGB
+// Blob from file - Opaque SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -396,7 +384,7 @@
     }).then(testImageBitmapOpaque);
 }, 'createImageBitmap in linear RGB from an opaque SRGB Blob with resize.');
 
-// Blob - Transparent SRGB
+// Blob form file - Transparent SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -409,8 +397,48 @@
     }).then(testImageBitmapTransparent);
 }, 'createImageBitmap in linear RGB from a transparent SRGB Blob with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the blob from P3 and Rec2020 images.
+// Color managed blob from canvas
+function testCreateImageBitmapFromColorManagedBlob(colorSpace, pixelFormat,
+                                                   isTransparent) {
+    let canvasPixelFormat = '8-8-8-8';
+    if (pixelFormat == 'uint16')
+        canvasPixelFormat = 'float16';
+    var testCanvas;
+    if (isTransparent)
+      testCanvas = initializeTestCanvasTransparent('srgb', canvasPixelFormat);
+    else
+      testCanvas = initializeTestCanvas('srgb', canvasPixelFormat);
+    var encodeOptions = {};
+    encodeOptions.quality = 1;
+    encodeOptions.type = 'image/png';
+    encodeOptions.colorSpace = colorSpace;
+    encodeOptions.pixelFormat = pixelFormat;
+
+    var t = async_test('createImageBitmap in linear RGB from color managed Blob' +
+        ' with resize. blobColorSpace: ' + colorSpace + ', blobPixelFormat: ' +
+        pixelFormat + ', transparency: ' + isTransparent);
+    testCanvas.convertToBlob(encodeOptions).then(
+        t.step_func_done(function(blob) {
+            if (isTransparent)
+                testImageBitmapTransparent(blob);
+            else
+                testImageBitmapOpaque(blob);
+        }));
+}
+
+function runAllCreateImageBitmapFromColorManagedBlobTests() {
+    var blobColorSpaces = ['srgb', 'rec2020', 'display-p3'];
+    var blobPixelFormats = ['8-8-8-8', 'uint16'];
+    var transparencies = [false, true];
+    for (var i = 0; i < blobColorSpaces.length; i++)
+        for (var j = 0; j < blobPixelFormats.length; j++)
+            for (var k = 0; k < transparencies.length; k++) {
+                testCreateImageBitmapFromColorManagedBlob(blobColorSpaces[i],
+                    blobPixelFormats[j], transparencies[k]);
+            }
+}
+
+runAllCreateImageBitmapFromColorManagedBlobTests();
 
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
index 45ca3da..032adf3 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
@@ -274,10 +274,6 @@
 }, 'createImageBitmap in P3 from a transparent SRGB HTMLImageElement (BMP, \
 ICO, PNG, WEBP) with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more test scenarios for loading the image element from P3
-// and Rec2020 images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // SVG Image - SRGB
@@ -291,10 +287,6 @@
     }).then(testImageBitmapFromSVG);
 }, 'createImageBitmap in P3 from a SRGB SVG image with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the image element from P3 and Rec2020
-// SVG images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLVideoElement - SRGB
@@ -308,10 +300,6 @@
     }).then(testImageBitmapFromVideo);
 }, 'createImageBitmap in P3 from a SRGB HTMLVideoElement with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the video element from P3 and Rec2020
-// videos.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLCanvasElement - Opaque SRGB
@@ -366,7 +354,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Blob - Opaque SRGB
+// Blob from file - Opaque SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -379,7 +367,7 @@
     }).then(testImageBitmapOpaque);
 }, 'createImageBitmap in P3 from an opaque SRGB Blob with resize.');
 
-// Blob - Transparent SRGB
+// Blob from file - Transparent SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -392,8 +380,48 @@
     }).then(testImageBitmapTransparent);
 }, 'createImageBitmap in P3 from a transparent SRGB Blob with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the blob from P3 and Rec2020 images.
+// Color managed blob from canvas
+function testCreateImageBitmapFromColorManagedBlob(colorSpace, pixelFormat,
+                                                   isTransparent) {
+    let canvasPixelFormat = '8-8-8-8';
+    if (pixelFormat == 'uint16')
+        canvasPixelFormat = 'float16';
+    var testCanvas;
+    if (isTransparent)
+      testCanvas = initializeTestCanvasTransparent('srgb', canvasPixelFormat);
+    else
+      testCanvas = initializeTestCanvas('srgb', canvasPixelFormat);
+    var encodeOptions = {};
+    encodeOptions.quality = 1;
+    encodeOptions.type = 'image/png';
+    encodeOptions.colorSpace = colorSpace;
+    encodeOptions.pixelFormat = pixelFormat;
+
+    var t = async_test('createImageBitmap in P3 from color managed Blob' +
+        ' with resize. blobColorSpace: ' + colorSpace + ', blobPixelFormat: ' +
+        pixelFormat + ', transparency: ' + isTransparent);
+    testCanvas.convertToBlob(encodeOptions).then(
+        t.step_func_done(function(blob) {
+            if (isTransparent)
+                testImageBitmapTransparent(blob);
+            else
+                testImageBitmapOpaque(blob);
+        }));
+}
+
+function runAllCreateImageBitmapFromColorManagedBlobTests() {
+    var blobColorSpaces = ['srgb', 'rec2020', 'display-p3'];
+    var blobPixelFormats = ['8-8-8-8', 'uint16'];
+    var transparencies = [false, true];
+    for (var i = 0; i < blobColorSpaces.length; i++)
+        for (var j = 0; j < blobPixelFormats.length; j++)
+            for (var k = 0; k < transparencies.length; k++) {
+                testCreateImageBitmapFromColorManagedBlob(blobColorSpaces[i],
+                    blobPixelFormats[j], transparencies[k]);
+            }
+}
+
+runAllCreateImageBitmapFromColorManagedBlobTests();
 
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
index 66cf1dd..a5b1584 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
@@ -288,10 +288,6 @@
 }, 'createImageBitmap in Rec2020 from a transparent SRGB HTMLImageElement (BMP, \
 ICO, PNG, WEBP) with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more test scenarios for loading the image element from P3
-// and Rec2020 images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // SVG Image - SRGB
@@ -305,10 +301,6 @@
     }).then(testImageBitmapFromSVG);
 }, 'createImageBitmap in Rec2020 from a SRGB SVG image with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the image element from P3 and Rec2020
-// SVG images.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLVideoElement - SRGB
@@ -322,10 +314,6 @@
     }).then(testImageBitmapFromVideo);
 }, 'createImageBitmap in Rec2020 from a SRGB HTMLVideoElement with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the video element from P3 and Rec2020
-// videos.
-
 ////////////////////////////////////////////////////////////////////////////////
 
 // HTMLCanvasElement - Opaque SRGB
@@ -380,7 +368,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Blob - Opaque SRGB
+// Blob from file - Opaque SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -393,7 +381,7 @@
     }).then(testImageBitmapOpaque);
 }, 'createImageBitmap in Rec2020 from an opaque SRGB Blob with resize.');
 
-// Blob - Transparent SRGB
+// Blob from file - Transparent SRGB
 promise_test(function() {
     return new Promise((resolve, reject) => {
         var xhr = new XMLHttpRequest();
@@ -406,8 +394,48 @@
     }).then(testImageBitmapTransparent);
 }, 'createImageBitmap in Rec2020 from a transparent SRGB Blob with resize.');
 
-// TODO(zakerinasab): crbug.com/668547
-// Add at least two more tests for loading the blob from P3 and Rec2020 images.
+// Color managed blob from canvas
+function testCreateImageBitmapFromColorManagedBlob(colorSpace, pixelFormat,
+                                                   isTransparent) {
+    let canvasPixelFormat = '8-8-8-8';
+    if (pixelFormat == 'uint16')
+        canvasPixelFormat = 'float16';
+    var testCanvas;
+    if (isTransparent)
+      testCanvas = initializeTestCanvasTransparent('srgb', canvasPixelFormat);
+    else
+      testCanvas = initializeTestCanvas('srgb', canvasPixelFormat);
+    var encodeOptions = {};
+    encodeOptions.quality = 1;
+    encodeOptions.type = 'image/png';
+    encodeOptions.colorSpace = colorSpace;
+    encodeOptions.pixelFormat = pixelFormat;
+
+    var t = async_test('createImageBitmap in Rec2020 from color managed Blob' +
+        ' with resize. blobColorSpace: ' + colorSpace + ', blobPixelFormat: ' +
+        pixelFormat + ', transparency: ' + isTransparent);
+    testCanvas.convertToBlob(encodeOptions).then(
+        t.step_func_done(function(blob) {
+            if (isTransparent)
+                testImageBitmapTransparent(blob);
+            else
+                testImageBitmapOpaque(blob);
+        }));
+}
+
+function runAllCreateImageBitmapFromColorManagedBlobTests() {
+    var blobColorSpaces = ['srgb', 'rec2020', 'display-p3'];
+    var blobPixelFormats = ['8-8-8-8', 'uint16'];
+    var transparencies = [false, true];
+    for (var i = 0; i < blobColorSpaces.length; i++)
+        for (var j = 0; j < blobPixelFormats.length; j++)
+            for (var k = 0; k < transparencies.length; k++) {
+                testCreateImageBitmapFromColorManagedBlob(blobColorSpaces[i],
+                    blobPixelFormats[j], transparencies[k]);
+            }
+}
+
+runAllCreateImageBitmapFromColorManagedBlobTests();
 
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/high-bit-depth-images.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/draw-high-bit-depth-images.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/fast/canvas/color-space/high-bit-depth-images.html
rename to third_party/WebKit/LayoutTests/fast/canvas/color-space/draw-high-bit-depth-images.html
diff --git a/third_party/WebKit/LayoutTests/fast/css/CSSStyleSheet-constructable.html b/third_party/WebKit/LayoutTests/fast/css/CSSStyleSheet-constructable.html
index bb45ad32..bc47187 100644
--- a/third_party/WebKit/LayoutTests/fast/css/CSSStyleSheet-constructable.html
+++ b/third_party/WebKit/LayoutTests/fast/css/CSSStyleSheet-constructable.html
@@ -397,4 +397,38 @@
   assert_throws('NotAllowedError', () => { document.adoptedStyleSheets = new StyleSheetList([iframeStyleSheet]); });
   assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)");
 }, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet and the AdoptedStyleSheets are in different document trees');
+
+
+test(() => {
+  const sheet = document.createCSSStyleSheetSync(redStyleTexts[0], {title: "Red", disabled: true, media: "screen, print"});
+  assert_equals(sheet.title, "Red");
+  assert_equals(sheet.ownerNode, null);
+  assert_equals(sheet.ownerRule, null);
+  assert_equals(sheet.media.length, 2);
+  assert_equals(sheet.media.item(0), "screen");
+  assert_equals(sheet.media.item(1), "print");
+  assert_true(sheet.disabled);
+  assert_equals(sheet.cssRules.length, 1);
+  assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]);
+
+  sheet.insertRule(redStyleTexts[1]);
+  assert_equals(sheet.cssRules.length, 2);
+  assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
+}, "Document.createCSSStyleSheetSync creates stylesheets synchronously");
+
+const import_text = '@import url("constructable-import.css");';
+test(() => {
+  assert_throws("NotAllowedError", () => { document.createCSSStyleSheetSync(import_text) });
+}, "Document.createCSSStyleSheetSync throws exception when there is import rule inside");
+
+
+promise_test(() => {
+  const sheet_promise = document.createCSSStyleSheet(import_text);
+  return sheet_promise.then(function(sheet) {
+    assert_equals(sheet.cssRules.length, 1);
+    assert_equals(sheet.cssRules[0].cssText, import_text);
+  });
+}, "Document.createCSSStyleSheet throws allows import rule inside");
+
 </script>
+
diff --git a/third_party/WebKit/LayoutTests/fast/css/constructable-import.css b/third_party/WebKit/LayoutTests/fast/css/constructable-import.css
new file mode 100644
index 0000000..60f1eab9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/constructable-import.css
@@ -0,0 +1,3 @@
+body {
+  color: red;
+}
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/midword-break-before-surrogate-pair-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/midword-break-before-surrogate-pair-expected.png
index 5a5eaaa5..12edf86 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/midword-break-before-surrogate-pair-expected.png
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/midword-break-before-surrogate-pair-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/borders/border-radius-mask-video-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/borders/border-radius-mask-video-expected.png
index 4bce27f..7435b7ddc 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/borders/border-radius-mask-video-expected.png
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/borders/border-radius-mask-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/table/backgr_border-table-quirks-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/table/backgr_border-table-quirks-expected.png
new file mode 100644
index 0000000..429d54e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/fast/table/backgr_border-table-quirks-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-from-baseline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-from-baseline-expected.txt
new file mode 100644
index 0000000..541b9f81
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-from-baseline-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 77, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 52, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 77, 50, 25],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 52, 50, 25],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-to-baseline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-to-baseline-expected.txt
new file mode 100644
index 0000000..541b9f81
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-items-change-to-baseline-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 77, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 52, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 77, 50, 25],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 52, 50, 25],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-from-baseline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-from-baseline-expected.txt
new file mode 100644
index 0000000..53a1886
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-from-baseline-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 77, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 52, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 127, 50, 25],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 77, 50, 25],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-to-baseline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-to-baseline-expected.txt
new file mode 100644
index 0000000..53a1886
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/css-grid-layout/align-self-change-to-baseline-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 77, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item2'",
+          "rect": [50, 52, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 127, 50, 25],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item1'",
+          "rect": [0, 77, 50, 25],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/details-marker-color-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/details-marker-color-change-expected.txt
new file mode 100644
index 0000000..428787f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/details-marker-color-change-expected.txt
@@ -0,0 +1,23 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Details'",
+          "rect": [24, 8, 45, 19],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutDetailsMarker DIV id='details-marker'",
+          "rect": [8, 12, 11, 11],
+          "reason": "style change"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png
deleted file mode 100644
index 95d9b53..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-reference-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-reference-change-expected.txt
new file mode 100644
index 0000000..3fafee4d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-reference-change-expected.txt
@@ -0,0 +1,18 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [8, 8, 110, 110],
+          "reason": "style change"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/backgroundfetch-origin-trial-interfaces.html b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/backgroundfetch-origin-trial-interfaces.html
new file mode 100644
index 0000000..8cedcb9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/backgroundfetch-origin-trial-interfaces.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Generate token with the command:
+generate_token.py http://127.0.0.1:8000 BackgroundFetch --expire-timestamp=2000000000
+-->
+
+<meta http-equiv="origin-trial" content="AtDl/AukAuUX0Sw7KRz+mrV2vpSYrfDyVS4vdO3I1clqoNgKGqCX5Np5KIhlC6oQl8XcULXJz5bc9Y4CcYj9xA4AAABXeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQmFja2dyb3VuZEZldGNoIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" />
+<title>Background Fetch API - interfaces exposed by origin trial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/origin-trials-helper.js"></script>
+<script src="/serviceworker/resources/test-helpers.js"></script>
+<script>
+
+test(t => {
+  OriginTrialsHelper.check_properties(this, {
+    'BackgroundFetchManager': ['fetch', 'get', 'getIds'],
+    'BackgroundFetchUpdateUIEvent': ['updateUI'],
+    'BackgroundFetchFetch': ['request'],
+    'BackgroundFetchRegistration': ['id', 'uploadTotal', 'uploaded',
+                                     'downloadTotal', 'downloaded', 'result',
+                                     'failureReason', 'recordsAvailable',
+                                     'onprogress'],
+    'ServiceWorkerRegistration': ['backgroundFetch'],
+  });
+}, 'BackgroundFetch API interfaces and properties in Origin-Trial enabled document.');
+
+fetch_tests_from_worker(new Worker('resources/backgroundfetch-origin-trial-interfaces-worker.js'));
+
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/resources/backgroundfetch-origin-trial-interfaces-worker.js b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/resources/backgroundfetch-origin-trial-interfaces-worker.js
new file mode 100644
index 0000000..6a65225
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/resources/backgroundfetch-origin-trial-interfaces-worker.js
@@ -0,0 +1,17 @@
+importScripts('/resources/testharness.js',
+              '/resources/origin-trials-helper.js');
+
+test(t => {
+
+  OriginTrialsHelper.check_properties(this, {
+       'ServiceWorkerRegistration': ['backgroundFetch'],
+       'BackgroundFetchFetch': ['request'],
+       'BackgroundFetchManager': ['fetch', 'get', 'getIds'],
+       'BackgroundFetchRegistration': ['id', 'uploadTotal', 'uploaded',
+                                       'downloadTotal', 'downloaded', 'result',
+                                       'failureReason', 'recordsAvailable',
+                                       'onprogress'],
+       });
+}, 'Background Fetch API interfaces and properties in Origin-Trial enabled worker.');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png
index 557e947..1c72bad 100644
--- a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png
index b9a7806..b7ed23c2 100644
--- a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
index 6763c036..1c72bad 100644
--- a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
index 05aa36e..b7ed23c2 100644
--- a/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
+++ b/third_party/WebKit/LayoutTests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/table/backgr_border-table-quirks-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/table/backgr_border-table-quirks-expected.txt
deleted file mode 100644
index 668bf420..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/table/backgr_border-table-quirks-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584 [color=#00FF00] [bgcolor=#333333]
-      LayoutBlockFlow {H3} at (0,0) size 784x17
-        LayoutText {#text} at (0,0) size 342x17
-          text run at (0,0) width 342: "crbug.com/35679: Background on 'table'"
-      LayoutBlockFlow {P} at (0,32.20) size 784x16
-        LayoutText {#text} at (0,0) size 656x16
-          text run at (0,0) width 656: "There should be three aqua stripes just inside the bottom and right table borders."
-      LayoutTable {TABLE} at (0,61.20) size 626x459 [color=#FFFFFF] [bgcolor=#000000] [border: (5px dotted #FFFFFF)]
-        LayoutBlockFlow {CAPTION} at (0,0) size 626x23
-          LayoutText {#text} at (167,0) size 292x22
-            text run at (167,0) width 292: "With 'border-collapse: separate'"
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableSection {THEAD} at (5,28) size 616x110
-          LayoutTableRow {TR} at (0,7) size 616x96
-            LayoutTableCell {TH} at (7,41) size 130x27 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=1]
-              LayoutText {#text} at (42,2) size 46x22
-                text run at (42,2) width 46: "TH A"
-            LayoutTableCell {TH} at (144,29) size 220x51 [border: (13px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (87,14) size 46x22
-                text run at (87,14) width 46: "TH B"
-            LayoutTableCell {TH} at (371,41) size 118x27 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (36,2) size 46x22
-                text run at (36,2) width 46: "TH C"
-            LayoutTableCell {TH} at (496,41) size 113x27 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (34,2) size 45x22
-                text run at (34,2) width 45: "TH D"
-        LayoutTableSection {TBODY} at (5,138) size 616x198
-          LayoutTableRow {TR} at (0,0) size 616x104
-            LayoutTableCell {TD} at (7,70) size 130x51 [border: (13px dotted #FFFFFF)] [r=0 c=0 rs=2 cs=1]
-              LayoutText {#text} at (14,14) size 44x22
-                text run at (14,14) width 44: "TD E"
-            LayoutTableCell {TD} at (144,38) size 220x27 [border: (1px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 43x22
-                text run at (2,2) width 43: "TD F"
-            LayoutTableCell {TD} at (371,38) size 118x27 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 46x22
-                text run at (2,2) width 46: "TD G"
-            LayoutTableCell {TD} at (496,38) size 113x27 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 45x22
-                text run at (2,2) width 45: "TD H"
-          LayoutTableRow {TR} at (0,111) size 616x80
-            LayoutTableCell {TD} at (144,137) size 220x27 [border: (1px dotted #FFFFFF)] [r=1 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 42x22
-                text run at (2,2) width 42: "TD J"
-            LayoutTableCell {TD} at (371,137) size 118x27 [border: (1px dotted #FFFFFF)] [r=1 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 45x22
-                text run at (2,2) width 45: "TD K"
-            LayoutTableCell {TD} at (496,137) size 113x27 [border: (1px dotted #FFFFFF)] [r=1 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 43x22
-                text run at (2,2) width 43: "TD L"
-        LayoutTableSection {TFOOT} at (5,336) size 616x118
-          LayoutTableRow {TR} at (0,0) size 616x111
-            LayoutTableCell {TD} at (7,42) size 357x27 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=2]
-              LayoutText {#text} at (2,2) size 47x22
-                text run at (2,2) width 47: "TD M"
-            LayoutTableCell {TD} at (371,42) size 118x27 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 46x22
-                text run at (2,2) width 46: "TD O"
-            LayoutTableCell {TD} at (496,42) size 113x27 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 44x22
-                text run at (2,2) width 44: "TD P"
-      LayoutBlockFlow {DIV} at (0,520.20) size 784x31
-        LayoutInline {A} at (0,0) size 88x16 [color=#FFFF00]
-          LayoutImage {IMG} at (0,0) size 88x31
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {ADDRESS} at (0,551.20) size 784x17
-        LayoutText {#text} at (0,0) size 704x17
-          text run at (0,0) width 704: "CSS2 Table Backgrounds Test Suite designed and written by fantasai <fantasai@escape.com>"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_border-table-quirks-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_border-table-quirks-expected.txt
deleted file mode 100644
index 489cd78..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/table/backgr_border-table-quirks-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584 [color=#00FF00] [bgcolor=#333333]
-      LayoutBlockFlow {H3} at (0,0) size 784x17
-        LayoutText {#text} at (0,0) size 347x17
-          text run at (0,0) width 347: "crbug.com/35679: Background on 'table'"
-      LayoutBlockFlow {P} at (0,32.20) size 784x15
-        LayoutText {#text} at (0,0) size 640x15
-          text run at (0,0) width 640: "There should be three aqua stripes just inside the bottom and right table borders."
-      LayoutTable {TABLE} at (0,60.20) size 626x458 [color=#FFFFFF] [bgcolor=#000000] [border: (5px dotted #FFFFFF)]
-        LayoutBlockFlow {CAPTION} at (0,0) size 626x22
-          LayoutText {#text} at (167,0) size 292x22
-            text run at (167,0) width 292: "With 'border-collapse: separate'"
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableSection {THEAD} at (5,27) size 616x110
-          LayoutTableRow {TR} at (0,7) size 616x96
-            LayoutTableCell {TH} at (7,42) size 130x26 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=1]
-              LayoutText {#text} at (42,2) size 46x22
-                text run at (42,2) width 46: "TH A"
-            LayoutTableCell {TH} at (144,30) size 220x50 [border: (13px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (87,14) size 46x22
-                text run at (87,14) width 46: "TH B"
-            LayoutTableCell {TH} at (371,42) size 118x26 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (36,2) size 46x22
-                text run at (36,2) width 46: "TH C"
-            LayoutTableCell {TH} at (496,42) size 113x26 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (34,2) size 45x22
-                text run at (34,2) width 45: "TH D"
-        LayoutTableSection {TBODY} at (5,137) size 616x198
-          LayoutTableRow {TR} at (0,0) size 616x104
-            LayoutTableCell {TD} at (7,70) size 130x50 [border: (13px dotted #FFFFFF)] [r=0 c=0 rs=2 cs=1]
-              LayoutText {#text} at (14,14) size 44x22
-                text run at (14,14) width 44: "TD E"
-            LayoutTableCell {TD} at (144,39) size 220x26 [border: (1px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 43x22
-                text run at (2,2) width 43: "TD F"
-            LayoutTableCell {TD} at (371,39) size 118x26 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 46x22
-                text run at (2,2) width 46: "TD G"
-            LayoutTableCell {TD} at (496,39) size 113x26 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 45x22
-                text run at (2,2) width 45: "TD H"
-          LayoutTableRow {TR} at (0,111) size 616x80
-            LayoutTableCell {TD} at (144,138) size 220x26 [border: (1px dotted #FFFFFF)] [r=1 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 42x22
-                text run at (2,2) width 42: "TD J"
-            LayoutTableCell {TD} at (371,138) size 118x26 [border: (1px dotted #FFFFFF)] [r=1 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 45x22
-                text run at (2,2) width 45: "TD K"
-            LayoutTableCell {TD} at (496,138) size 113x26 [border: (1px dotted #FFFFFF)] [r=1 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 43x22
-                text run at (2,2) width 43: "TD L"
-        LayoutTableSection {TFOOT} at (5,335) size 616x118
-          LayoutTableRow {TR} at (0,0) size 616x111
-            LayoutTableCell {TD} at (7,42) size 357x26 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=2]
-              LayoutText {#text} at (2,2) size 47x22
-                text run at (2,2) width 47: "TD M"
-            LayoutTableCell {TD} at (371,42) size 118x26 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 46x22
-                text run at (2,2) width 46: "TD O"
-            LayoutTableCell {TD} at (496,42) size 113x26 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 44x22
-                text run at (2,2) width 44: "TD P"
-      LayoutBlockFlow {DIV} at (0,518.20) size 784x31
-        LayoutInline {A} at (0,0) size 88x15 [color=#FFFF00]
-          LayoutImage {IMG} at (0,0) size 88x31
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {ADDRESS} at (0,549.20) size 784x15
-        LayoutText {#text} at (0,0) size 687x15
-          text run at (0,0) width 687: "CSS2 Table Backgrounds Test Suite designed and written by fantasai <fantasai@escape.com>"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/table/backgr_border-table-quirks-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/table/backgr_border-table-quirks-expected.txt
deleted file mode 100644
index dec8cd5..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/fast/table/backgr_border-table-quirks-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584 [color=#00FF00] [bgcolor=#333333]
-      LayoutBlockFlow {H3} at (0,0) size 784x17
-        LayoutText {#text} at (0,0) size 342x17
-          text run at (0,0) width 342: "crbug.com/35679: Background on 'table'"
-      LayoutBlockFlow {P} at (0,32.20) size 784x16
-        LayoutText {#text} at (0,0) size 656x16
-          text run at (0,0) width 656: "There should be three aqua stripes just inside the bottom and right table borders."
-      LayoutTable {TABLE} at (0,61.20) size 626x463 [color=#FFFFFF] [bgcolor=#000000] [border: (5px dotted #FFFFFF)]
-        LayoutBlockFlow {CAPTION} at (0,0) size 626x27
-          LayoutText {#text} at (147,0) size 332x27
-            text run at (147,0) width 332: "With 'border-collapse: separate'"
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableSection {THEAD} at (5,32) size 616x110
-          LayoutTableRow {TR} at (0,7) size 616x96
-            LayoutTableCell {TH} at (7,41) size 130x27 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=1]
-              LayoutText {#text} at (43,2) size 44x22
-                text run at (43,2) width 44: "TH A"
-            LayoutTableCell {TH} at (144,29) size 220x51 [border: (13px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (87,14) size 46x22
-                text run at (87,14) width 46: "TH B"
-            LayoutTableCell {TH} at (371,41) size 118x27 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (36,2) size 46x22
-                text run at (36,2) width 46: "TH C"
-            LayoutTableCell {TH} at (496,41) size 113x27 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (34,2) size 45x22
-                text run at (34,2) width 45: "TH D"
-        LayoutTableSection {TBODY} at (5,142) size 616x198
-          LayoutTableRow {TR} at (0,0) size 616x104
-            LayoutTableCell {TD} at (7,68) size 130x55 [border: (13px dotted #FFFFFF)] [r=0 c=0 rs=2 cs=1]
-              LayoutText {#text} at (14,14) size 49x27
-                text run at (14,14) width 49: "TD E"
-            LayoutTableCell {TD} at (144,36) size 220x31 [border: (1px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD F"
-            LayoutTableCell {TD} at (371,36) size 118x31 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD G"
-            LayoutTableCell {TD} at (496,36) size 113x31 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD H"
-          LayoutTableRow {TR} at (0,111) size 616x80
-            LayoutTableCell {TD} at (144,135) size 220x31 [border: (1px dotted #FFFFFF)] [r=1 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD J"
-            LayoutTableCell {TD} at (371,135) size 118x31 [border: (1px dotted #FFFFFF)] [r=1 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD K"
-            LayoutTableCell {TD} at (496,135) size 113x31 [border: (1px dotted #FFFFFF)] [r=1 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD L"
-        LayoutTableSection {TFOOT} at (5,340) size 616x118
-          LayoutTableRow {TR} at (0,0) size 616x111
-            LayoutTableCell {TD} at (7,40) size 357x31 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=2]
-              LayoutText {#text} at (2,2) size 53x27
-                text run at (2,2) width 53: "TD M"
-            LayoutTableCell {TD} at (371,40) size 118x31 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD O"
-            LayoutTableCell {TD} at (496,40) size 113x31 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 49x27
-                text run at (2,2) width 49: "TD P"
-      LayoutBlockFlow {DIV} at (0,524.20) size 784x31
-        LayoutInline {A} at (0,0) size 88x16 [color=#FFFF00]
-          LayoutImage {IMG} at (0,0) size 88x31
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {ADDRESS} at (0,555.20) size 784x17
-        LayoutText {#text} at (0,0) size 704x17
-          text run at (0,0) width 704: "CSS2 Table Backgrounds Test Suite designed and written by fantasai <fantasai@escape.com>"
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/table/backgr_border-table-quirks-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/fast/table/backgr_border-table-quirks-expected.txt
deleted file mode 100644
index 75b4bed..0000000
--- a/third_party/WebKit/LayoutTests/platform/win7/fast/table/backgr_border-table-quirks-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584 [color=#00FF00] [bgcolor=#333333]
-      LayoutBlockFlow {H3} at (0,0) size 784x17
-        LayoutText {#text} at (0,0) size 342x17
-          text run at (0,0) width 342: "crbug.com/35679: Background on 'table'"
-      LayoutBlockFlow {P} at (0,32.20) size 784x16
-        LayoutText {#text} at (0,0) size 656x16
-          text run at (0,0) width 656: "There should be three aqua stripes just inside the bottom and right table borders."
-      LayoutTable {TABLE} at (0,61.20) size 626x463 [color=#FFFFFF] [bgcolor=#000000] [border: (5px dotted #FFFFFF)]
-        LayoutBlockFlow {CAPTION} at (0,0) size 626x27
-          LayoutText {#text} at (148,0) size 330x27
-            text run at (148,0) width 330: "With 'border-collapse: separate'"
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableCol {COLGROUP} at (0,0) size 0x0
-          LayoutTableCol {COL} at (0,0) size 0x0
-        LayoutTableSection {THEAD} at (5,32) size 616x110
-          LayoutTableRow {TR} at (0,7) size 616x96
-            LayoutTableCell {TH} at (7,41) size 130x27 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=1]
-              LayoutText {#text} at (43,2) size 44x22
-                text run at (43,2) width 44: "TH A"
-            LayoutTableCell {TH} at (144,29) size 220x51 [border: (13px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (87,14) size 46x22
-                text run at (87,14) width 46: "TH B"
-            LayoutTableCell {TH} at (371,41) size 118x27 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (36,2) size 46x22
-                text run at (36,2) width 46: "TH C"
-            LayoutTableCell {TH} at (496,41) size 113x27 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (34,2) size 45x22
-                text run at (34,2) width 45: "TH D"
-        LayoutTableSection {TBODY} at (5,142) size 616x198
-          LayoutTableRow {TR} at (0,0) size 616x104
-            LayoutTableCell {TD} at (7,68) size 130x55 [border: (13px dotted #FFFFFF)] [r=0 c=0 rs=2 cs=1]
-              LayoutText {#text} at (14,14) size 49x27
-                text run at (14,14) width 49: "TD E"
-            LayoutTableCell {TD} at (144,36) size 220x31 [border: (1px dotted #FFFFFF)] [r=0 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD F"
-            LayoutTableCell {TD} at (371,36) size 118x31 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD G"
-            LayoutTableCell {TD} at (496,36) size 113x31 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD H"
-          LayoutTableRow {TR} at (0,111) size 616x80
-            LayoutTableCell {TD} at (144,135) size 220x31 [border: (1px dotted #FFFFFF)] [r=1 c=1 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD J"
-            LayoutTableCell {TD} at (371,135) size 118x31 [border: (1px dotted #FFFFFF)] [r=1 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD K"
-            LayoutTableCell {TD} at (496,135) size 113x31 [border: (1px dotted #FFFFFF)] [r=1 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 48x27
-                text run at (2,2) width 48: "TD L"
-        LayoutTableSection {TFOOT} at (5,340) size 616x118
-          LayoutTableRow {TR} at (0,0) size 616x111
-            LayoutTableCell {TD} at (7,40) size 357x31 [border: (1px dotted #FFFFFF)] [r=0 c=0 rs=1 cs=2]
-              LayoutText {#text} at (2,2) size 53x27
-                text run at (2,2) width 53: "TD M"
-            LayoutTableCell {TD} at (371,40) size 118x31 [border: (1px dotted #FFFFFF)] [r=0 c=2 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 51x27
-                text run at (2,2) width 51: "TD O"
-            LayoutTableCell {TD} at (496,40) size 113x31 [border: (1px dotted #FFFFFF)] [r=0 c=3 rs=1 cs=1]
-              LayoutText {#text} at (2,2) size 49x27
-                text run at (2,2) width 49: "TD P"
-      LayoutBlockFlow {DIV} at (0,524.20) size 784x31
-        LayoutInline {A} at (0,0) size 88x16 [color=#FFFF00]
-          LayoutImage {IMG} at (0,0) size 88x31
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {ADDRESS} at (0,555.20) size 784x17
-        LayoutText {#text} at (0,0) size 704x17
-          text run at (0,0) width 704: "CSS2 Table Backgrounds Test Suite designed and written by fantasai <fantasai@escape.com>"
diff --git a/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm-expected.svg b/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm-expected.svg
new file mode 100644
index 0000000..0352c6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm-expected.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <div style="width: 100px; height: 100px; background-color: green"></div>
+</html>
diff --git a/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm.svg b/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm.svg
new file mode 100644
index 0000000..bc7ef29
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/zoom/page/zoom-svg-doc-no-svg-docelm.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <script src="../../../resources/run-after-layout-and-paint.js"/>
+  <div style="width: 50px; height: 50px; background-color: green"/>
+  <script>
+    runAfterLayoutAndPaint(function() {
+      eventSender.setPageZoomFactor(2);
+    }, true);
+  </script>
+</html>
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 2e8b314..598c34de 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -1638,6 +1638,7 @@
     method createAttributeNS
     method createCDATASection
     method createCSSStyleSheet
+    method createCSSStyleSheetSync
     method createComment
     method createDocumentFragment
     method createElement
diff --git a/third_party/blink/public/platform/DEPS b/third_party/blink/public/platform/DEPS
index efa764a..a10dc67 100644
--- a/third_party/blink/public/platform/DEPS
+++ b/third_party/blink/public/platform/DEPS
@@ -25,6 +25,7 @@
     "+net/http",
     "+services/network/public/cpp/cors/cors_error_status.h",
     "+services/network/public/cpp/cors/preflight_result.h",
+    "+services/network/public/cpp/cors/preflight_timing_info.h",
     "+services/network/public/cpp/shared_url_loader_factory.h",
 
     # Enforce to use mojom-shared.h in blink/public so that it can compile
diff --git a/third_party/blink/public/platform/modules/background_fetch/background_fetch.mojom b/third_party/blink/public/platform/modules/background_fetch/background_fetch.mojom
index 3e3b635..3e78d8b 100644
--- a/third_party/blink/public/platform/modules/background_fetch/background_fetch.mojom
+++ b/third_party/blink/public/platform/modules/background_fetch/background_fetch.mojom
@@ -103,7 +103,9 @@
   OnProgress(uint64 upload_total,
              uint64 uploaded,
              uint64 download_total,
-             uint64 downloaded);
+             uint64 downloaded,
+             BackgroundFetchResult result,
+             BackgroundFetchFailureReason failure_reason);
 
   // Notifies the BackgroundFetchRegistration that the data for the associated
   // fetch is no longer available. The mojo connection will be closed after
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 2bc098f..627fe7b 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2041,6 +2041,7 @@
   kGetComputedStyleForWebkitAppearance = 2588,
   kCursorImageLE32x32 = 2589,
   kCursorImageGT32x32 = 2590,
+  kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics = 2591,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_url_loader_client.h b/third_party/blink/public/platform/web_url_loader_client.h
index f1e64d8..04d2885 100644
--- a/third_party/blink/public/platform/web_url_loader_client.h
+++ b/third_party/blink/public/platform/web_url_loader_client.h
@@ -34,6 +34,7 @@
 #include <memory>
 #include "base/time/time.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_data_consumer_handle.h"
 #include "third_party/blink/public/platform/web_referrer_policy.h"
@@ -107,11 +108,13 @@
   // will be generated in devtools console if this flag is set to true.
   // TODO(crbug.com/798625): use different callback for subresources
   // with responses blocked due to document protection.
-  virtual void DidFinishLoading(base::TimeTicks finish_time,
-                                int64_t total_encoded_data_length,
-                                int64_t total_encoded_body_length,
-                                int64_t total_decoded_body_length,
-                                bool should_report_corb_blocking) {}
+  virtual void DidFinishLoading(
+      base::TimeTicks finish_time,
+      int64_t total_encoded_data_length,
+      int64_t total_encoded_body_length,
+      int64_t total_decoded_body_length,
+      bool should_report_corb_blocking,
+      const std::vector<network::cors::PreflightTimingInfo>&) {}
 
   // Called when the load completes with an error.
   // |total_encoded_data_length| may be equal to kUnknownEncodedDataLength.
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
index ddc0dda9..62c993e 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -41,7 +41,6 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
index bb71bea..42b029d 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
@@ -25,6 +25,8 @@
     v8::Local<v8::Function> listener,
     const V8PrivateProperty::Symbol& property) {
   DCHECK(!HasCompiledHandler());
+  v8::Context::BackupIncumbentScope backup_incumbent_scope(
+      script_state->GetContext());
   event_handler_ = V8EventHandlerNonNull::Create(listener);
   Attach(script_state, listener, property, this);
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc
index de79b9b..cc79a175 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc
@@ -197,7 +197,6 @@
 
 std::unique_ptr<SourceLocation>
 JSEventHandlerForContentAttribute::GetSourceLocation(EventTarget& target) {
-  v8::HandleScope(GetIsolate());
   auto source_location = JSEventHandler::GetSourceLocation(target);
   if (source_location)
     return source_location;
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index 09013e6..7d033b09 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -116,7 +116,9 @@
   void AppendPadding() { AppendPadding(GetResource()); }
 
   void Finish() {
-    GetResource()->Loader()->DidFinishLoading(TimeTicks(), 0, 0, 0, false);
+    GetResource()->Loader()->DidFinishLoading(
+        TimeTicks(), 0, 0, 0, false,
+        std::vector<network::cors::PreflightTimingInfo>());
     GetResource()->SetStatus(ResourceStatus::kCached);
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
index 4d7a66f..0f01f1c 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
@@ -71,10 +71,10 @@
 
   // Used by WorkerThread. Returns true if the context is successfully
   // initialized or already initialized.
-  // For WorkerGlobalScope and ThreadedWorkletGlobalScope, |url_for_debugger| is
-  // and should be used only for setting name/origin that appears in DevTools.
-  // For other global scopes, |human_readable_name| is used for setting
-  // DOMWrapperWorld's human readable name.
+  // For WorkerGlobalScope and threaded WorkletGlobalScope, |url_for_debugger|
+  // is and should be used only for setting name/origin that appears in
+  // DevTools. For other global scopes, |human_readable_name| is used for
+  // setting DOMWrapperWorld's human readable name.
   bool InitializeContextIfNeeded(const String& human_readable_name,
                                  const KURL& url_for_debugger);
 
diff --git a/third_party/blink/renderer/core/css/css_style_sheet.cc b/third_party/blink/renderer/core/css/css_style_sheet.cc
index 222a3086..b564ea1 100644
--- a/third_party/blink/renderer/core/css/css_style_sheet.cc
+++ b/third_party/blink/renderer/core/css/css_style_sheet.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/renderer/core/css/media_list.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
@@ -527,12 +528,19 @@
     contents_->ClientLoadStarted(this);
 }
 
-void CSSStyleSheet::SetText(const String& text) {
+void CSSStyleSheet::SetText(const String& text,
+                            bool allow_import_rules,
+                            ExceptionState& exception_state) {
   child_rule_cssom_wrappers_.clear();
 
   CSSStyleSheet::RuleMutationScope mutation_scope(this);
   contents_->ClearRules();
-  contents_->ParseString(text);
+  if (contents_->ParseString(text, allow_import_rules) ==
+      ParseSheetResult::kHasUnallowedImportRule) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "@import rules are not allowed when "
+                                      "creating stylesheet synchronously.");
+  }
 }
 
 void CSSStyleSheet::SetAlternateFromConstructor(
diff --git a/third_party/blink/renderer/core/css/css_style_sheet.h b/third_party/blink/renderer/core/css/css_style_sheet.h
index 7dcf15e..dcafec0c 100644
--- a/third_party/blink/renderer/core/css/css_style_sheet.h
+++ b/third_party/blink/renderer/core/css/css_style_sheet.h
@@ -184,7 +184,7 @@
   bool SheetLoaded();
   bool LoadCompleted() const { return load_completed_; }
   void StartLoadingDynamicSheet();
-  void SetText(const String&);
+  void SetText(const String&, bool allow_import_rules, ExceptionState&);
   void SetMedia(MediaList*);
   void SetAlternateFromConstructor(bool);
   bool IsAlternate() const;
@@ -235,6 +235,7 @@
   // For other CSSStyleSheet, consult the alternate attribute.
   bool alternate_from_constructor_ = false;
   bool enable_rule_access_for_inspector_ = false;
+
   String title_;
   scoped_refptr<MediaQuerySet> media_queries_;
   MediaQueryResultList viewport_dependent_media_query_results_;
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index a753f3f..9f354dd 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -65,12 +65,14 @@
                                   CSSParserImpl::kAllowImportRules);
 }
 
-void CSSParser::ParseSheet(const CSSParserContext* context,
-                           StyleSheetContents* style_sheet,
-                           const String& text,
-                           CSSDeferPropertyParsing defer_property_parsing) {
-  return CSSParserImpl::ParseStyleSheet(text, context, style_sheet,
-                                        defer_property_parsing);
+ParseSheetResult CSSParser::ParseSheet(
+    const CSSParserContext* context,
+    StyleSheetContents* style_sheet,
+    const String& text,
+    CSSDeferPropertyParsing defer_property_parsing,
+    bool allow_import_rules) {
+  return CSSParserImpl::ParseStyleSheet(
+      text, context, style_sheet, defer_property_parsing, allow_import_rules);
 }
 
 void CSSParser::ParseSheetForInspector(const CSSParserContext* context,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.h b/third_party/blink/renderer/core/css/parser/css_parser.h
index 40471cc4..118bb97 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser.h
@@ -22,6 +22,7 @@
 class StyleRuleKeyframe;
 class StyleSheetContents;
 class CSSValue;
+enum class ParseSheetResult;
 enum class SecureContextMode;
 
 // This class serves as the public API for the css/parser subsystem
@@ -34,11 +35,13 @@
                                   StyleSheetContents*,
                                   const String&);
 
-  static void ParseSheet(const CSSParserContext*,
-                         StyleSheetContents*,
-                         const String&,
-                         CSSDeferPropertyParsing defer_property_parsing =
-                             CSSDeferPropertyParsing::kNo);
+  static ParseSheetResult ParseSheet(
+      const CSSParserContext*,
+      StyleSheetContents*,
+      const String&,
+      CSSDeferPropertyParsing defer_property_parsing =
+          CSSDeferPropertyParsing::kNo,
+      bool allow_import_rules = true);
   static CSSSelectorList ParseSelector(const CSSParserContext*,
                                        StyleSheetContents*,
                                        const String&);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 3e2de46a..a38daa0 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -242,11 +242,12 @@
   return rule;
 }
 
-void CSSParserImpl::ParseStyleSheet(
+ParseSheetResult CSSParserImpl::ParseStyleSheet(
     const String& string,
     const CSSParserContext* context,
     StyleSheetContents* style_sheet,
-    CSSDeferPropertyParsing defer_property_parsing) {
+    CSSDeferPropertyParsing defer_property_parsing,
+    bool allow_import_rules) {
   TRACE_EVENT_BEGIN2("blink,blink_style", "CSSParserImpl::parseStyleSheet",
                      "baseUrl", context->BaseURL().GetString().Utf8(), "mode",
                      context->Mode());
@@ -260,10 +261,16 @@
     parser.lazy_state_ =
         new CSSLazyParsingState(context, string, parser.style_sheet_);
   }
+  ParseSheetResult result = ParseSheetResult::kSucceeded;
   bool first_rule_valid = parser.ConsumeRuleList(
-      stream, kTopLevelRuleList, [&style_sheet](StyleRuleBase* rule) {
+      stream, kTopLevelRuleList,
+      [&style_sheet, &result, allow_import_rules](StyleRuleBase* rule) {
         if (rule->IsCharsetRule())
           return;
+        if (rule->IsImportRule() && !allow_import_rules) {
+          result = ParseSheetResult::kHasUnallowedImportRule;
+          return;
+        }
         style_sheet->ParserAppendRule(rule);
       });
   style_sheet->SetHasSyntacticallyValidCSSHeader(first_rule_valid);
@@ -272,6 +279,7 @@
   TRACE_EVENT_END2("blink,blink_style", "CSSParserImpl::parseStyleSheet",
                    "tokenCount", tokenizer.TokenCount(), "length",
                    string.length());
+  return result;
 }
 
 CSSSelectorList CSSParserImpl::ParsePageSelector(
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
index e53aa95..37bf28a0 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -38,6 +38,11 @@
 class StyleSheetContents;
 class Element;
 
+enum class ParseSheetResult {
+  kSucceeded,
+  kHasUnallowedImportRule,
+};
+
 class CSSParserImpl {
   STACK_ALLOCATED();
 
@@ -95,11 +100,12 @@
                                   const CSSParserContext*,
                                   StyleSheetContents*,
                                   AllowedRulesType);
-  static void ParseStyleSheet(
+  static ParseSheetResult ParseStyleSheet(
       const String&,
       const CSSParserContext*,
       StyleSheetContents*,
-      CSSDeferPropertyParsing = CSSDeferPropertyParsing::kNo);
+      CSSDeferPropertyParsing = CSSDeferPropertyParsing::kNo,
+      bool allow_import_rules = true);
   static CSSSelectorList ParsePageSelector(CSSParserTokenRange,
                                            StyleSheetContents*);
 
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.cc b/third_party/blink/renderer/core/css/style_sheet_contents.cc
index 2ec89e30..0725d4d7 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.cc
@@ -377,16 +377,21 @@
   parse_histogram.CountMicroseconds(parse_duration);
 }
 
-void StyleSheetContents::ParseString(const String& sheet_text) {
-  ParseStringAtPosition(sheet_text, TextPosition::MinimumPosition());
+ParseSheetResult StyleSheetContents::ParseString(const String& sheet_text,
+                                                 bool allow_import_rules) {
+  return ParseStringAtPosition(sheet_text, TextPosition::MinimumPosition(),
+                               allow_import_rules);
 }
 
-void StyleSheetContents::ParseStringAtPosition(
+ParseSheetResult StyleSheetContents::ParseStringAtPosition(
     const String& sheet_text,
-    const TextPosition& start_position) {
+    const TextPosition& start_position,
+    bool allow_import_rules) {
   const CSSParserContext* context =
       CSSParserContext::CreateWithStyleSheetContents(ParserContext(), this);
-  CSSParser::ParseSheet(context, this, sheet_text);
+  return CSSParser::ParseSheet(context, this, sheet_text,
+                               CSSDeferPropertyParsing::kNo,
+                               allow_import_rules);
 }
 
 bool StyleSheetContents::IsLoading() const {
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.h b/third_party/blink/renderer/core/css/style_sheet_contents.h
index 2d0ebf2bd..acf6156 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.h
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.h
@@ -44,6 +44,7 @@
 class StyleRuleFontFace;
 class StyleRuleImport;
 class StyleRuleNamespace;
+enum class ParseSheetResult;
 
 class CORE_EXPORT StyleSheetContents
     : public GarbageCollectedFinalized<StyleSheetContents> {
@@ -72,8 +73,10 @@
 
   void ParseAuthorStyleSheet(const CSSStyleSheetResource*,
                              const SecurityOrigin*);
-  void ParseString(const String&);
-  void ParseStringAtPosition(const String&, const TextPosition&);
+  ParseSheetResult ParseString(const String&, bool allow_import_rules = true);
+  ParseSheetResult ParseStringAtPosition(const String&,
+                                         const TextPosition&,
+                                         bool allow_import_rules = true);
 
   bool IsCacheableForResource() const;
   bool IsCacheableForStyleElement() const;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 882a1f35..4ee3c52b 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1143,12 +1143,33 @@
   // at once here because CSS parsing is done synchronously on the main thread.
   // TODO(rakina): Find a way to improve this.
   CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
-  sheet->SetText(text);
+  sheet->SetText(text, true /* allow_import_rules */, exception_state);
   sheet->SetAssociatedDocument(this);
   return ScriptPromise::Cast(script_state,
                              ScriptValue::From(script_state, sheet));
 }
 
+CSSStyleSheet* Document::createCSSStyleSheetSync(
+    ScriptState* script_state,
+    const String& text,
+    ExceptionState& exception_state) {
+  return Document::createCSSStyleSheetSync(
+      script_state, text, CSSStyleSheetInit(), exception_state);
+}
+
+CSSStyleSheet* Document::createCSSStyleSheetSync(
+    ScriptState* script_state,
+    const String& text,
+    const CSSStyleSheetInit& options,
+    ExceptionState& exception_state) {
+  CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
+  sheet->SetText(text, false /* allow_import_rules */, exception_state);
+  if (exception_state.HadException())
+    return nullptr;
+  sheet->SetAssociatedDocument(this);
+  return sheet;
+}
+
 CSSStyleSheet* Document::createEmptyCSSStyleSheet(
     ScriptState* script_state,
     const CSSStyleSheetInit& options,
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index a78b81b..e5e5704 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -377,6 +377,15 @@
                                     const CSSStyleSheetInit&,
                                     ExceptionState&);
 
+  CSSStyleSheet* createCSSStyleSheetSync(ScriptState*,
+                                         const String&,
+                                         const CSSStyleSheetInit&,
+                                         ExceptionState&);
+
+  CSSStyleSheet* createCSSStyleSheetSync(ScriptState*,
+                                         const String&,
+                                         ExceptionState&);
+
   Element* ElementFromPoint(double x, double y) const;
   HeapVector<Member<Element>> ElementsFromPoint(double x, double y) const;
   Range* caretRangeFromPoint(int x, int y);
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index b822ea1..83d8beb 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -75,6 +75,7 @@
     [NewObject] Range createRange();
 
     [CallWith=ScriptState, NewObject, RaisesException, RuntimeEnabled=ConstructableStylesheets] Promise<CSSStyleSheet> createCSSStyleSheet(DOMString text, optional CSSStyleSheetInit options);
+    [CallWith=ScriptState, NewObject, RaisesException, RuntimeEnabled=ConstructableStylesheets] CSSStyleSheet createCSSStyleSheetSync(DOMString text, optional CSSStyleSheetInit options);
     [CallWith=ScriptState, NewObject, RaisesException, RuntimeEnabled=ConstructableStylesheets] CSSStyleSheet createEmptyCSSStyleSheet(optional CSSStyleSheetInit options);
 
 
diff --git a/third_party/blink/renderer/core/frame/ad_tracker.cc b/third_party/blink/renderer/core/frame/ad_tracker.cc
index e42b890..89f672f 100644
--- a/third_party/blink/renderer/core/frame/ad_tracker.cc
+++ b/third_party/blink/renderer/core/frame/ad_tracker.cc
@@ -9,13 +9,29 @@
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/core_probe_sink.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
+namespace {
+
+bool IsKnownAdExecutionContext(ExecutionContext* execution_context) {
+  // TODO(jkarlin): Do the same check for worker contexts.
+  if (auto* document = DynamicTo<Document>(execution_context)) {
+    LocalFrame* frame = document->GetFrame();
+    if (frame && frame->IsAdSubframe())
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
 AdTracker::AdTracker(LocalFrame* local_root) : local_root_(local_root) {
   local_root_->GetProbeSink()->addAdTracker(this);
 }
@@ -110,6 +126,11 @@
   if (!execution_context)
     return false;
 
+  // If we're in an ad context, then no matter what the executing script is it's
+  // considered an ad.
+  if (IsKnownAdExecutionContext(execution_context))
+    return true;
+
   // The pseudo-stack contains entry points into the stack (e.g., when v8 is
   // executed) but not the entire stack. It's cheap to retrieve the top of the
   // stack so scan that as well.
@@ -131,6 +152,12 @@
   if (!execution_context)
     return false;
 
+  // TODO(jkarlin): Minor memory optimization, stop tracking known ad scripts in
+  // ad contexts. This will reduce the size of executing_scripts_. Note that
+  // this is a minor win, as the strings are already ref-counted.
+  if (IsKnownAdExecutionContext(execution_context))
+    return true;
+
   auto it = known_ad_scripts_.find(execution_context);
   if (it == known_ad_scripts_.end())
     return false;
diff --git a/third_party/blink/renderer/core/frame/ad_tracker_test.cc b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
index 8ff1084..c253444f 100644
--- a/third_party/blink/renderer/core/frame/ad_tracker_test.cc
+++ b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
@@ -211,6 +211,65 @@
   EXPECT_TRUE(ad_tracker_->RequestWithUrlTaggedAsAd(kVanillaUrl));
 }
 
+// Unknown script running in an ad context should be labeled as ad script.
+TEST_F(AdTrackerSimTest, ScriptDetectedByContext) {
+  const char kAdScriptUrl[] = "https://example.com/ad_script.js";
+  SimRequest ad_script(kAdScriptUrl, "text/javascript");
+
+  ad_tracker_->SetAdSuffix("ad_script.js");
+
+  // Create an iframe that's considered an ad.
+  main_resource_->Complete("<body><script src='ad_script.js'></script></body>");
+  ad_script.Complete(R"SCRIPT(
+    frame = document.createElement("iframe");
+    document.body.appendChild(frame);
+    )SCRIPT");
+
+  // The child frame should be an ad subframe.
+  LocalFrame* child_frame =
+      ToLocalFrame(GetDocument().GetFrame()->Tree().FirstChild());
+  EXPECT_TRUE(child_frame->IsAdSubframe());
+
+  // Now run unknown script in the child's context. It should be considered an
+  // ad based on context alone.
+  ad_tracker_->SetExecutionContext(child_frame->GetDocument());
+  ad_tracker_->SetScriptAtTopOfStack("foo.js");
+  EXPECT_TRUE(ad_tracker_->IsAdScriptInStack());
+}
+
+// When inline script in an ad frame inserts an iframe into a non-ad frame, the
+// new frame should be considered an ad.
+TEST_F(AdTrackerSimTest, InlineAdScriptRunningInNonAdContext) {
+  SimRequest ad_script("https://example.com/ad_script.js", "text/javascript");
+  SimRequest ad_iframe("https://example.com/ad_frame.html", "text/html");
+  ad_tracker_->SetAdSuffix("ad_script.js");
+
+  main_resource_->Complete("<body><script src='ad_script.js'></script></body>");
+  ad_script.Complete(R"SCRIPT(
+    frame = document.createElement("iframe");
+    frame.src = "ad_frame.html";
+    document.body.appendChild(frame);
+    )SCRIPT");
+
+  // Verify that the new frame is an ad frame.
+  EXPECT_TRUE(ToLocalFrame(GetDocument().GetFrame()->Tree().FirstChild())
+                  ->IsAdSubframe());
+
+  // Create a new sibling frame to the ad frame. The ad context calls the non-ad
+  // context's (top frame) appendChild.
+  ad_iframe.Complete(R"HTML(
+    <script>
+      frame = document.createElement("iframe");
+      frame.name = "ad_sibling";
+      parent.document.body.appendChild(frame);
+    </script>
+    )HTML");
+
+  // The new sibling frame should also be identified as an ad.
+  EXPECT_TRUE(ToLocalFrame(GetDocument().GetFrame()->Tree().Find("ad_sibling"))
+                  ->IsAdSubframe());
+}
+
 // Image loaded by ad script is tagged as ad.
 TEST_F(AdTrackerSimTest, ImageLoadedWhileExecutingAdScript) {
   const char kAdUrl[] = "https://example.com/ad_script.js";
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index de328b1b..9c7cffd 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -609,6 +609,19 @@
               WillBeRemoved("Cache.addAll() with duplicate requests", kM72,
                             "5622587912617984")};
 
+    case WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics:
+      return {"RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics", kM72,
+              String::Format(
+                  "\"Complex\" Plan B SDP detected! Chrome will switch the "
+                  "default sdpSemantics in %s from 'plan-b' to the "
+                  "standardized 'unified-plan' format and this peer connection "
+                  "is relying on the default sdpSemantics. This SDP is not "
+                  "compatible with Unified Plan and will be rejected by "
+                  "clients expecting Unified Plan. For more information about "
+                  "how to prepare for the switch, see "
+                  "https://webrtc.org/web-apis/chrome/unified-plan/.",
+                  MilestoneString(kM72))};
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
       return {"NotDeprecated", kUnknown, ""};
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 2bd4094..339d96d 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
 #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
 #include "third_party/blink/renderer/core/editing/suggestion/text_suggestion_controller.h"
+#include "third_party/blink/renderer/core/events/current_input_event.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/frame/ad_tracker.h"
 #include "third_party/blink/renderer/core/frame/content_settings_client.h"
@@ -458,6 +459,9 @@
         nullptr,
         loader_.ResourceRequestForReload(load_type, client_redirect_policy));
     request.SetClientRedirect(client_redirect_policy);
+    if (const WebInputEvent* input_event = CurrentInputEvent::Get()) {
+      request.SetInputStartTime(input_event->TimeStamp());
+    }
     loader_.StartNavigation(request, load_type);
   } else {
     DCHECK_EQ(WebFrameLoadType::kReload, load_type);
diff --git a/third_party/blink/renderer/core/frame/use_counter_test.cc b/third_party/blink/renderer/core/frame/use_counter_test.cc
index 7670a6c4b..abdd32b 100644
--- a/third_party/blink/renderer/core/frame/use_counter_test.cc
+++ b/third_party/blink/renderer/core/frame/use_counter_test.cc
@@ -321,6 +321,46 @@
   EXPECT_TRUE(UseCounter::IsCounted(document, feature));
 }
 
+TEST_F(UseCounterTest, CSSFlexibleBox) {
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
+  Document& document = dummy_page_holder->GetDocument();
+  WebFeature feature = WebFeature::kCSSFlexibleBox;
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+  document.documentElement()->SetInnerHTMLFromString(
+      "<div style='display: flex;'>flexbox</div>");
+  document.View()->UpdateAllLifecyclePhases();
+  EXPECT_TRUE(UseCounter::IsCounted(document, feature));
+}
+
+TEST_F(UseCounterTest, CSSFlexibleBoxInline) {
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
+  Document& document = dummy_page_holder->GetDocument();
+  WebFeature feature = WebFeature::kCSSFlexibleBox;
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+  document.documentElement()->SetInnerHTMLFromString(
+      "<div style='display: inline-flex;'>flexbox</div>");
+  document.View()->UpdateAllLifecyclePhases();
+  EXPECT_TRUE(UseCounter::IsCounted(document, feature));
+}
+
+TEST_F(UseCounterTest, CSSFlexibleBoxButton) {
+  // LayoutButton is a subclass of LayoutFlexibleBox, however we don't want it
+  // to be counted as usage of flexboxes as it's an implementation detail.
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
+  Document& document = dummy_page_holder->GetDocument();
+  WebFeature feature = WebFeature::kCSSFlexibleBox;
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+  document.documentElement()->SetInnerHTMLFromString("<button>button</button>");
+  document.View()->UpdateAllLifecyclePhases();
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+}
+
 class DeprecationTest : public testing::Test {
  public:
   DeprecationTest()
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.cc b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
index 008ee9d..1be8a7e 100644
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.cc
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
@@ -13,40 +13,23 @@
 float TextMetrics::GetFontBaseline(const TextBaseline& text_baseline,
                                    const SimpleFontData& font_data) {
   FontMetrics font_metrics = font_data.GetFontMetrics();
-  // If the font is so tiny that the lroundf operations result in two
-  // different types of text baselines to return the same baseline, use
-  // floating point metrics (crbug.com/338908).
-  // If you changed the heuristic here, for consistency please also change it
-  // in SimpleFontData::platformInit().
-  bool use_float_ascent_descent = font_data.EmHeightAscent().ToInt() < 4;
   switch (text_baseline) {
     case kTopTextBaseline:
-      return use_float_ascent_descent
-                 ? font_data.EmHeightAscent().ToFloat()
-                 : lround(font_data.EmHeightAscent().ToFloat());
+      return font_data.EmHeightAscent().ToFloat();
     case kHangingTextBaseline:
       // According to
       // http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
       // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of
       // the ascender height"
-      return use_float_ascent_descent
-                 ? font_metrics.FloatAscent() * kHangingAsPercentOfAscent / 100
-                 : font_metrics.Ascent() * kHangingAsPercentOfAscent / 100;
+      return font_metrics.FloatAscent() * kHangingAsPercentOfAscent / 100.0;
     case kIdeographicTextBaseline:
-      return use_float_ascent_descent ? -font_metrics.FloatDescent()
-                                      : -font_metrics.Descent();
+      return -font_metrics.FloatDescent();
     case kBottomTextBaseline:
-      return use_float_ascent_descent
-                 ? -font_data.EmHeightDescent().ToFloat()
-                 : -lround(font_data.EmHeightDescent().ToFloat());
+      return -font_data.EmHeightDescent().ToFloat();
     case kMiddleTextBaseline:
-      return use_float_ascent_descent
-                 ? (-font_data.EmHeightDescent() + font_data.EmHeightAscent())
-                           .ToFloat() /
-                       2.0f
-                 : (-lround(font_data.EmHeightDescent().ToFloat()) +
-                    lround(font_data.EmHeightAscent().ToFloat())) /
-                       2;
+      return (font_data.EmHeightAscent().ToFloat() -
+              font_data.EmHeightDescent().ToFloat()) /
+             2.0f;
     case kAlphabeticTextBaseline:
     default:
       // Do nothing.
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index a2e86069..7426bd9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -1005,9 +1005,11 @@
   return url.IsEmpty() ? document_url_ : url;
 }
 
-bool InspectorStyleSheet::SetText(const String& text, ExceptionState&) {
+bool InspectorStyleSheet::SetText(const String& text,
+                                  ExceptionState& exception_state) {
   InnerSetText(text, true);
-  page_style_sheet_->SetText(text);
+  page_style_sheet_->SetText(text, true /* allow_import_rules */,
+                             exception_state);
   OnStyleSheetTextChanged();
   return true;
 }
diff --git a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
index a6db1eb..1af591c7 100644
--- a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
@@ -60,7 +60,7 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/timing/memory_info.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/core/xml/xpath_evaluator.h"
 #include "third_party/blink/renderer/core/xml/xpath_result.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
@@ -82,7 +82,7 @@
   if (auto* document = DynamicTo<Document>(context))
     return document->GetFrame();
   if (context->IsMainThreadWorkletGlobalScope())
-    return ToMainThreadWorkletGlobalScope(context)->GetFrame();
+    return ToWorkletGlobalScope(context)->GetFrame();
   return nullptr;
 }
 }
@@ -179,12 +179,11 @@
     script_state =
         event->World() ? ToScriptState(frame, *event->World()) : nullptr;
   } else if (context->IsMainThreadWorkletGlobalScope()) {
-    frame = ToMainThreadWorkletGlobalScope(context)->GetFrame();
+    frame = ToWorkletGlobalScope(context)->GetFrame();
     if (!frame)
       return;
-    script_state = ToMainThreadWorkletGlobalScope(context)
-                       ->ScriptController()
-                       ->GetScriptState();
+    script_state =
+        ToWorkletGlobalScope(context)->ScriptController()->GetScriptState();
   } else {
     NOTREACHED();
   }
diff --git a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
index 55ea1c13c..32c028f 100644
--- a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
 #include "third_party/blink/renderer/core/inspector/v8_inspector_string.h"
 #include "third_party/blink/renderer/core/inspector/worker_inspector_controller.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
diff --git a/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc b/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
index e4ae9b6e..e94d9fc 100644
--- a/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
+++ b/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
@@ -66,7 +66,7 @@
 CSSLayoutDefinition::~CSSLayoutDefinition() = default;
 
 CSSLayoutDefinition::Instance::Instance(CSSLayoutDefinition* definition,
-                                        v8::Local<v8::Object> instance)
+                                        v8::Local<v8::Value> instance)
     : definition_(definition),
       instance_(definition->script_state_->GetIsolate(), instance) {}
 
@@ -84,7 +84,7 @@
   ScriptState::Scope scope(script_state);
 
   v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::Object> instance = instance_.NewLocal(isolate);
+  v8::Local<v8::Value> instance = instance_.NewLocal(isolate);
   v8::Local<v8::Context> context = script_state->GetContext();
 
   v8::Local<v8::Function> layout = definition_->layout_.NewLocal(isolate);
@@ -310,8 +310,9 @@
   v8::Local<v8::Function> constructor = constructor_.NewLocal(isolate);
   DCHECK(!IsUndefinedOrNull(constructor));
 
-  v8::Local<v8::Object> layout_instance;
-  if (V8ObjectConstructor::NewInstance(isolate, constructor)
+  v8::Local<v8::Value> layout_instance;
+  if (V8ScriptRunner::CallAsConstructor(
+          isolate, constructor, ExecutionContext::From(script_state_), 0, {})
           .ToLocal(&layout_instance)) {
     instance = new Instance(this, layout_instance);
   } else {
diff --git a/third_party/blink/renderer/core/layout/custom/css_layout_definition.h b/third_party/blink/renderer/core/layout/custom/css_layout_definition.h
index ffba10e7..bec75b9 100644
--- a/third_party/blink/renderer/core/layout/custom/css_layout_definition.h
+++ b/third_party/blink/renderer/core/layout/custom/css_layout_definition.h
@@ -43,7 +43,7 @@
   // CSSLayoutDefinition.
   class Instance final : public GarbageCollectedFinalized<Instance> {
    public:
-    Instance(CSSLayoutDefinition*, v8::Local<v8::Object> instance);
+    Instance(CSSLayoutDefinition*, v8::Local<v8::Value> instance);
 
     // Runs the web developer defined layout, returns true if everything
     // succeeded. It populates the FragmentResultOptions dictionary, and
@@ -58,7 +58,7 @@
     void ReportException(ExceptionState*);
 
     Member<CSSLayoutDefinition> definition_;
-    ScopedPersistent<v8::Object> instance_;
+    ScopedPersistent<v8::Value> instance_;
   };
 
   // Creates an instance of the web developer defined class. May return a
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
index b5dde5c3..01d4e46d 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
@@ -44,9 +44,7 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerReportingProxy& reporting_proxy,
     PendingLayoutRegistry* pending_layout_registry)
-    : MainThreadWorkletGlobalScope(frame,
-                                   std::move(creation_params),
-                                   reporting_proxy),
+    : WorkletGlobalScope(std::move(creation_params), reporting_proxy, frame),
       pending_layout_registry_(pending_layout_registry) {}
 
 LayoutWorkletGlobalScope::~LayoutWorkletGlobalScope() = default;
@@ -55,7 +53,7 @@
   MainThreadDebugger::Instance()->ContextWillBeDestroyed(
       ScriptController()->GetScriptState());
 
-  MainThreadWorkletGlobalScope::Dispose();
+  WorkletGlobalScope::Dispose();
 }
 
 // https://drafts.css-houdini.org/css-layout-api/#dom-layoutworkletglobalscope-registerlayout
@@ -161,7 +159,7 @@
 void LayoutWorkletGlobalScope::Trace(blink::Visitor* visitor) {
   visitor->Trace(layout_definitions_);
   visitor->Trace(pending_layout_registry_);
-  MainThreadWorkletGlobalScope::Trace(visitor);
+  WorkletGlobalScope::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h
index a351858..b66123b3 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/layout/custom/pending_layout_registry.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
@@ -17,8 +17,7 @@
 class CSSLayoutDefinition;
 class WorkerReportingProxy;
 
-class CORE_EXPORT LayoutWorkletGlobalScope final
-    : public MainThreadWorkletGlobalScope {
+class CORE_EXPORT LayoutWorkletGlobalScope final : public WorkletGlobalScope {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(LayoutWorkletGlobalScope);
 
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 0922fc49..6fdc8c1 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -60,8 +60,6 @@
       has_definite_height_(SizeDefiniteness::kUnknown),
       in_layout_(false) {
   DCHECK(!ChildrenInline());
-  if (!IsAnonymous())
-    UseCounter::Count(GetDocument(), WebFeature::kCSSFlexibleBox);
 }
 
 LayoutFlexibleBox::~LayoutFlexibleBox() = default;
diff --git a/third_party/blink/renderer/core/layout/layout_grid.cc b/third_party/blink/renderer/core/layout/layout_grid.cc
index d5e2450..5b944636 100644
--- a/third_party/blink/renderer/core/layout/layout_grid.cc
+++ b/third_party/blink/renderer/core/layout/layout_grid.cc
@@ -49,8 +49,6 @@
       grid_(Grid::Create(this)),
       track_sizing_algorithm_(this, *grid_) {
   DCHECK(!ChildrenInline());
-  if (!IsAnonymous())
-    UseCounter::Count(GetDocument(), WebFeature::kCSSGridLayout);
 }
 
 LayoutGrid::~LayoutGrid() = default;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 34be920..8c557fe 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -268,9 +268,11 @@
       return new LayoutDeprecatedFlexibleBox(*element);
     case EDisplay::kFlex:
     case EDisplay::kInlineFlex:
+      UseCounter::Count(element->GetDocument(), WebFeature::kCSSFlexibleBox);
       return LayoutObjectFactory::CreateFlexibleBox(*element, style);
     case EDisplay::kGrid:
     case EDisplay::kInlineGrid:
+      UseCounter::Count(element->GetDocument(), WebFeature::kCSSGridLayout);
       return new LayoutGrid(element);
     case EDisplay::kLayoutCustom:
     case EDisplay::kInlineLayoutCustom:
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
index f5f7541..6c178a27 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader_registry.h"
 #include "third_party/blink/renderer/core/script/modulator.h"
 #include "third_party/blink/renderer/core/script/module_script.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
index 546a991..d2c856f 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -23,8 +23,8 @@
 #include "third_party/blink/renderer/core/testing/dummy_modulator.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/main_thread_worklet_reporting_proxy.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
@@ -157,7 +157,7 @@
   ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
   std::unique_ptr<MainThreadWorkletReportingProxy> reporting_proxy_;
   Persistent<ModuleScriptLoaderTestModulator> modulator_;
-  Persistent<MainThreadWorkletGlobalScope> global_scope_;
+  Persistent<WorkletGlobalScope> global_scope_;
 };
 
 void ModuleScriptLoaderTest::SetUp() {
@@ -191,8 +191,8 @@
       OriginTrialContext::GetTokens(&GetDocument()).get(),
       base::UnguessableToken::Create(), nullptr /* worker_settings */,
       kV8CacheOptionsDefault, new WorkletModuleResponsesMap);
-  global_scope_ = new MainThreadWorkletGlobalScope(
-      &GetFrame(), std::move(creation_params), *reporting_proxy_);
+  global_scope_ = new WorkletGlobalScope(std::move(creation_params),
+                                         *reporting_proxy_, &GetFrame());
   global_scope_->ScriptController()->InitializeContextIfNeeded("Dummy Context",
                                                                NullURL());
   modulator_ = new ModuleScriptLoaderTestModulator(
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index fc163ab88..24a6612 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -399,7 +399,9 @@
   if (!all_data_received && Loader()) {
     // Observers are notified via ImageResource::finish().
     // TODO(hiroshige): Do not call didFinishLoading() directly.
-    Loader()->DidFinishLoading(CurrentTimeTicks(), size, size, size, false);
+    Loader()->DidFinishLoading(
+        CurrentTimeTicks(), size, size, size, false,
+        std::vector<network::cors::PreflightTimingInfo>());
   } else {
     auto result = GetContent()->UpdateImage(
         nullptr, GetStatus(),
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index 740aecd..d2b51d1f 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -226,8 +226,9 @@
   image_resource->Loader()->DidReceiveResponse(
       WrappedResourceResponse(resource_response));
   image_resource->Loader()->DidReceiveData(data, kDataLength);
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), kDataLength,
-                                             kDataLength, kDataLength, false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), kDataLength, kDataLength, kDataLength, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 
   // Checks |imageResource|'s status after reloading.
   EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
@@ -285,7 +286,8 @@
   image_resource->Loader()->DidFinishLoading(
       TimeTicks(), kJpegImageSubrangeWithDimensionsLength,
       kJpegImageSubrangeWithDimensionsLength,
-      kJpegImageSubrangeWithDimensionsLength, false);
+      kJpegImageSubrangeWithDimensionsLength, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 
   // Checks that |imageResource| is successfully loaded, showing a placeholder.
   EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
@@ -325,9 +327,9 @@
       WrappedResourceResponse(resource_response));
   image_resource->Loader()->DidReceiveData(
       reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), sizeof(kJpegImage),
-                                             sizeof(kJpegImage),
-                                             sizeof(kJpegImage), false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
+      false, std::vector<network::cors::PreflightTimingInfo>());
 
   // Checks that |imageResource| is successfully loaded,
   // showing a non-placeholder image.
@@ -421,7 +423,9 @@
 
   // This part finishes. The image is created, callbacks are sent, and the data
   // buffer is cleared.
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), 0, 0, 0, false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), 0, 0, 0, false,
+      std::vector<network::cors::PreflightTimingInfo>());
   EXPECT_TRUE(image_resource->ResourceBuffer());
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -464,7 +468,9 @@
   image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
                              sizeof(kJpegImage));
   image_resource->AppendData(kBoundary, strlen(kBoundary));
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), 0, 0, 0, false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), 0, 0, 0, false,
+      std::vector<network::cors::PreflightTimingInfo>());
   EXPECT_TRUE(image_resource->GetContent()->HasImage());
   EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
   EXPECT_TRUE(image_resource->GetContent()
@@ -811,9 +817,9 @@
       WrappedResourceResponse(resource_response));
   image_resource->Loader()->DidReceiveData(
       reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), sizeof(kJpegImage),
-                                             sizeof(kJpegImage),
-                                             sizeof(kJpegImage), false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
+      false, std::vector<network::cors::PreflightTimingInfo>());
 
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
   EXPECT_EQ(image_resource, fetcher->CachedResource(test_url));
@@ -1315,7 +1321,9 @@
   EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
   EXPECT_EQ(0, observer->ImageChangedCount());
 
-  image_resource->Loader()->DidFinishLoading(TimeTicks(), 0, 0, 0, false);
+  image_resource->Loader()->DidFinishLoading(
+      TimeTicks(), 0, 0, 0, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 
   EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
@@ -1362,7 +1370,8 @@
   image_resource->Loader()->DidFinishLoading(
       TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
       kJpegImageSubrangeWithoutDimensionsLength,
-      kJpegImageSubrangeWithoutDimensionsLength, false);
+      kJpegImageSubrangeWithoutDimensionsLength, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 
   EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
@@ -1592,7 +1601,8 @@
     image_resource->Loader()->DidFinishLoading(
         TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
         kJpegImageSubrangeWithoutDimensionsLength,
-        kJpegImageSubrangeWithoutDimensionsLength, false);
+        kJpegImageSubrangeWithoutDimensionsLength, false,
+        std::vector<network::cors::PreflightTimingInfo>());
 
     EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
     EXPECT_EQ(2, observer->ImageChangedCount());
@@ -1731,9 +1741,9 @@
         WrappedResourceResponse(resource_response));
     image_resource->Loader()->DidReceiveData(
         reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
-    image_resource->Loader()->DidFinishLoading(TimeTicks(), sizeof(kJpegImage),
-                                               sizeof(kJpegImage),
-                                               sizeof(kJpegImage), false);
+    image_resource->Loader()->DidFinishLoading(
+        TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
+        false, std::vector<network::cors::PreflightTimingInfo>());
 
     EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
     EXPECT_EQ(sizeof(kJpegImage), image_resource->EncodedSize());
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index 3d3f5683..94645c02 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -493,6 +493,9 @@
   // TODO(japhet): Form submissions on RemoteFrames don't work yet.
   FrameLoadRequest new_request(nullptr, request.GetResourceRequest());
   new_request.SetForm(request.Form());
+  if (const WebInputEvent* input_event = CurrentInputEvent::Get()) {
+    new_request.SetInputStartTime(input_event->TimeStamp());
+  }
   auto blob_url_token = request.GetBlobURLToken();
   if (blob_url_token)
     new_request.SetBlobURLToken(std::move(blob_url_token));
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index 491ca9b..fdeee5f 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -20,7 +20,7 @@
 
 // Set a big enough limit for the number of nodes to ensure memory usage is
 // capped. Exceeding such limit will deactivate the algorithm.
-constexpr size_t kNodeNumberLimit = 5000;
+constexpr size_t kImageNodeNumberLimit = 5000;
 
 static bool LargeImageOnTop(const base::WeakPtr<ImageRecord>& a,
                             const base::WeakPtr<ImageRecord>& b) {
@@ -197,7 +197,7 @@
   const LayoutImage* image = ToLayoutImage(&object);
   if (!id_record_map_.Contains(node_id)) {
     recorded_node_count_++;
-    if (recorded_node_count_ < kNodeNumberLimit) {
+    if (recorded_node_count_ < kImageNodeNumberLimit) {
       LayoutRect invalidated_rect = image->FirstFragment().VisualRect();
       // Do not record first size until invalidated_rect's size becomes
       // non-empty.
@@ -226,7 +226,7 @@
       latest_image_heap_.push(record->AsWeakPtr());
       id_record_map_.insert(node_id, std::move(record));
     } else {
-      // for assessing whether kNodeNumberLimit is large enough for all
+      // for assessing whether kImageNodeNumberLimit is large enough for all
       // normal cases
       TRACE_EVENT_INSTANT1("loading", "ImagePaintTimingDetector::OverNodeLimit",
                            TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index 06c99469..6d0cd0b 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -20,7 +20,7 @@
 
 // Calculate metrics candidate every 1 second after the first text pre-paint.
 static constexpr TimeDelta kTimerDelay = TimeDelta::FromSeconds(1);
-constexpr size_t kNodeNumberLimit = 5000;
+constexpr size_t kTextNodeNumberLimit = 5000;
 
 static bool LargeTextOnTop(const std::unique_ptr<TextRecord>& a,
                            const std::unique_ptr<TextRecord>& b) {
@@ -164,8 +164,8 @@
 
   // We deactivate the algorithm if the number of nodes exceeds limitation.
   recorded_node_count_++;
-  if (recorded_node_count_ > kNodeNumberLimit) {
-    // for assessing whether kNodeNumberLimit is large enough for all
+  if (recorded_node_count_ > kTextNodeNumberLimit) {
+    // for assessing whether kTextNodeNumberLimit is large enough for all
     // normal cases
     TRACE_EVENT_INSTANT1("loading", "TextPaintTimingDetector::OverNodeLimit",
                          TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 3d23acb..a948d68 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -1558,37 +1558,6 @@
   return *TransitionsInternal();
 }
 
-const Font& ComputedStyle::GetFont() const {
-  return FontInternal();
-}
-const FontDescription& ComputedStyle::GetFontDescription() const {
-  return FontInternal().GetFontDescription();
-}
-float ComputedStyle::SpecifiedFontSize() const {
-  return GetFontDescription().SpecifiedSize();
-}
-float ComputedStyle::ComputedFontSize() const {
-  return GetFontDescription().ComputedSize();
-}
-LayoutUnit ComputedStyle::ComputedFontSizeAsFixed() const {
-  return LayoutUnit::FromFloatRound(GetFontDescription().ComputedSize());
-}
-int ComputedStyle::FontSize() const {
-  return GetFontDescription().ComputedPixelSize();
-}
-float ComputedStyle::FontSizeAdjust() const {
-  return GetFontDescription().SizeAdjust();
-}
-bool ComputedStyle::HasFontSizeAdjust() const {
-  return GetFontDescription().HasSizeAdjust();
-}
-FontSelectionValue ComputedStyle::GetFontWeight() const {
-  return GetFontDescription().Weight();
-}
-FontSelectionValue ComputedStyle::GetFontStretch() const {
-  return GetFontDescription().Stretch();
-}
-
 FontBaseline ComputedStyle::GetFontBaseline() const {
   // TODO(kojii): Incorporate 'dominant-baseline' when we support it.
   // https://www.w3.org/TR/css-inline-3/#dominant-baseline-property
@@ -1731,13 +1700,6 @@
   return GetRegisteredVariable(name, true);
 }
 
-float ComputedStyle::WordSpacing() const {
-  return GetFontDescription().WordSpacing();
-}
-float ComputedStyle::LetterSpacing() const {
-  return GetFontDescription().LetterSpacing();
-}
-
 bool ComputedStyle::SetFontDescription(const FontDescription& v) {
   if (FontInternal().GetFontDescription() != v) {
     SetFontInternal(Font(v));
@@ -1746,10 +1708,6 @@
   return false;
 }
 
-void ComputedStyle::SetFont(const Font& font) {
-  SetFontInternal(font);
-}
-
 bool ComputedStyle::HasIdenticalAscentDescentAndLineGap(
     const ComputedStyle& other) const {
   const SimpleFontData* font_data = GetFont().PrimaryFont();
@@ -1759,10 +1717,6 @@
              other_font_data->GetFontMetrics());
 }
 
-const Length& ComputedStyle::SpecifiedLineHeight() const {
-  return LineHeightInternal();
-}
-
 Length ComputedStyle::LineHeight() const {
   const Length& lh = LineHeightInternal();
   // Unlike getFontDescription().computedSize() and hence fontSize(), this is
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 31420083..ce17d0e5 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -899,27 +899,41 @@
   }
 
   // Font properties.
-  CORE_EXPORT const Font& GetFont() const;
-  CORE_EXPORT void SetFont(const Font&);
-  CORE_EXPORT const FontDescription& GetFontDescription() const;
+  CORE_EXPORT const Font& GetFont() const { return FontInternal(); }
+  CORE_EXPORT void SetFont(const Font& font) { SetFontInternal(font); }
+  CORE_EXPORT const FontDescription& GetFontDescription() const {
+    return FontInternal().GetFontDescription();
+  }
   CORE_EXPORT bool SetFontDescription(const FontDescription&);
   bool HasIdenticalAscentDescentAndLineGap(const ComputedStyle& other) const;
 
   // font-size
-  int FontSize() const;
-  CORE_EXPORT float SpecifiedFontSize() const;
-  CORE_EXPORT float ComputedFontSize() const;
-  LayoutUnit ComputedFontSizeAsFixed() const;
+  int FontSize() const { return GetFontDescription().ComputedPixelSize(); }
+  CORE_EXPORT float SpecifiedFontSize() const {
+    return GetFontDescription().SpecifiedSize();
+  }
+  CORE_EXPORT float ComputedFontSize() const {
+    return GetFontDescription().ComputedSize();
+  }
+  LayoutUnit ComputedFontSizeAsFixed() const {
+    return LayoutUnit::FromFloatRound(GetFontDescription().ComputedSize());
+  }
 
   // font-size-adjust
-  float FontSizeAdjust() const;
-  bool HasFontSizeAdjust() const;
+  float FontSizeAdjust() const { return GetFontDescription().SizeAdjust(); }
+  bool HasFontSizeAdjust() const {
+    return GetFontDescription().HasSizeAdjust();
+  }
 
   // font-weight
-  CORE_EXPORT FontSelectionValue GetFontWeight() const;
+  CORE_EXPORT FontSelectionValue GetFontWeight() const {
+    return GetFontDescription().Weight();
+  }
 
   // font-stretch
-  FontSelectionValue GetFontStretch() const;
+  FontSelectionValue GetFontStretch() const {
+    return GetFontDescription().Stretch();
+  }
 
   // Child is aligned to the parent by matching the parent’s dominant baseline
   // to the same baseline in the child.
@@ -933,7 +947,7 @@
 
   // FIXME: Remove letter-spacing/word-spacing and replace them with respective
   // FontBuilder calls.  letter-spacing
-  float LetterSpacing() const;
+  float LetterSpacing() const { return GetFontDescription().LetterSpacing(); }
   void SetLetterSpacing(float);
 
   // tab-size
@@ -949,7 +963,7 @@
   }
 
   // word-spacing
-  float WordSpacing() const;
+  float WordSpacing() const { return GetFontDescription().WordSpacing(); }
   void SetWordSpacing(float);
 
   // orphans
@@ -1327,7 +1341,7 @@
   void ApplyTextTransform(String*, UChar previous_character = ' ') const;
 
   // Line-height utility functions.
-  const Length& SpecifiedLineHeight() const;
+  const Length& SpecifiedLineHeight() const { return LineHeightInternal(); }
   int ComputedLineHeight() const;
   LayoutUnit ComputedLineHeightAsFixed() const;
 
diff --git a/third_party/blink/renderer/core/svg/svg_document_extensions.cc b/third_party/blink/renderer/core/svg/svg_document_extensions.cc
index 7c1efbc..3283831 100644
--- a/third_party/blink/renderer/core/svg/svg_document_extensions.cc
+++ b/third_party/blink/renderer/core/svg/svg_document_extensions.cc
@@ -140,9 +140,8 @@
 }
 
 bool SVGDocumentExtensions::ZoomAndPanEnabled() const {
-  if (SVGSVGElement* svg = rootElement(*document_))
-    return svg->ZoomAndPanEnabled();
-  return false;
+  SVGSVGElement* svg = rootElement(*document_);
+  return !svg || svg->ZoomAndPanEnabled();
 }
 
 void SVGDocumentExtensions::StartPan(const FloatPoint& start) {
diff --git a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_opaque.png b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_opaque.png
index 557e947..1c72bad 100644
--- a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_opaque.png
+++ b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_transparent.png b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_transparent.png
index b9a7806..b7ed23c2 100644
--- a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_transparent.png
+++ b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
index 6763c036..1c72bad 100644
--- a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
+++ b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png
Binary files differ
diff --git a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
index 05aa36e..b7ed23c2 100644
--- a/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
+++ b/third_party/blink/renderer/core/testing/data/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png
Binary files differ
diff --git a/third_party/blink/renderer/core/testing/sim/sim_network.cc b/third_party/blink/renderer/core/testing/sim/sim_network.cc
index ad25aa1..45df1df 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_network.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_network.cc
@@ -79,7 +79,8 @@
   if (!current_request_) {
     client->DidFinishLoading(finish_time, total_encoded_data_length,
                              total_encoded_body_length,
-                             total_decoded_body_length, false);
+                             total_decoded_body_length, false,
+                             std::vector<network::cors::PreflightTimingInfo>());
     return;
   }
   current_request_ = nullptr;
diff --git a/third_party/blink/renderer/core/testing/sim/sim_request.cc b/third_party/blink/renderer/core/testing/sim/sim_request.cc
index e6ff8a79..29db248 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_request.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_request.cc
@@ -68,9 +68,10 @@
                      total_encoded_data_length_, total_encoded_data_length_);
   } else {
     // TODO(esprehn): Is claiming a request time of 0 okay for tests?
-    client_->DidFinishLoading(TimeTicks(), total_encoded_data_length_,
-                              total_encoded_data_length_,
-                              total_encoded_data_length_, false);
+    client_->DidFinishLoading(
+        TimeTicks(), total_encoded_data_length_, total_encoded_data_length_,
+        total_encoded_data_length_, false,
+        std::vector<network::cors::PreflightTimingInfo>());
   }
   Reset();
 }
diff --git a/third_party/blink/renderer/core/workers/BUILD.gn b/third_party/blink/renderer/core/workers/BUILD.gn
index d0c15c1..3c36f4d 100644
--- a/third_party/blink/renderer/core/workers/BUILD.gn
+++ b/third_party/blink/renderer/core/workers/BUILD.gn
@@ -30,8 +30,6 @@
     "global_scope_creation_params.h",
     "installed_scripts_manager.cc",
     "installed_scripts_manager.h",
-    "main_thread_worklet_global_scope.cc",
-    "main_thread_worklet_global_scope.h",
     "main_thread_worklet_reporting_proxy.cc",
     "main_thread_worklet_reporting_proxy.h",
     "parent_execution_context_task_runners.cc",
@@ -51,8 +49,6 @@
     "threaded_messaging_proxy_base.h",
     "threaded_object_proxy_base.cc",
     "threaded_object_proxy_base.h",
-    "threaded_worklet_global_scope.cc",
-    "threaded_worklet_global_scope.h",
     "threaded_worklet_messaging_proxy.cc",
     "threaded_worklet_messaging_proxy.h",
     "threaded_worklet_object_proxy.cc",
diff --git a/third_party/blink/renderer/core/workers/README.md b/third_party/blink/renderer/core/workers/README.md
index 06723ad..067971989 100644
--- a/third_party/blink/renderer/core/workers/README.md
+++ b/third_party/blink/renderer/core/workers/README.md
@@ -18,7 +18,7 @@
 - `WorkerOrWorklet` prefix: Classes commonly used for workers and worklets (e.g., `WorkerOrWorkletGlobalScope`).
 - `Worker` / `Worklet` prefix: Classes used for workers or worklets (e.g., `WorkerGlobalScope`).
 - `Threaded` prefix: Classes used for workers and threaded worklets (e.g., `ThreadedMessagingProxyBase`).
-- `MainThreadWorklet` prefix: Classes used for main thread worklets (e.g., `MainThreadWorkletGlobalScope`).
+- `MainThreadWorklet` prefix: Classes used for main thread worklets (e.g., `MainThreadWorkletReportingProxy`).
 
 Thread hopping between the main (parent) thread and a worker thread is handled by proxy classes.
 
diff --git a/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.cc
deleted file mode 100644
index 55cf44e7..0000000
--- a/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.cc
+++ /dev/null
@@ -1,64 +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.
-
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
-
-#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/frame/deprecation.h"
-#include "third_party/blink/renderer/core/frame/frame_console.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
-#include "third_party/blink/renderer/core/probe/core_probes.h"
-#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-
-namespace blink {
-
-MainThreadWorkletGlobalScope::MainThreadWorkletGlobalScope(
-    LocalFrame* frame,
-    std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    WorkerReportingProxy& reporting_proxy)
-    : WorkletGlobalScope(std::move(creation_params),
-                         ToIsolate(frame),
-                         reporting_proxy),
-      ContextClient(frame) {
-  BindContentSecurityPolicyToExecutionContext();
-}
-
-MainThreadWorkletGlobalScope::~MainThreadWorkletGlobalScope() = default;
-
-WorkerThread* MainThreadWorkletGlobalScope::GetThread() const {
-  NOTREACHED();
-  return nullptr;
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-MainThreadWorkletGlobalScope::GetTaskRunner(TaskType type) {
-  DCHECK(IsContextThread());
-  // MainThreadWorkletGlobalScope lives on the main thread and its GetThread()
-  // doesn't return a valid worker thread. Instead, retrieve a task runner
-  // from the frame.
-  return GetFrame()->GetFrameScheduler()->GetTaskRunner(type);
-}
-
-void MainThreadWorkletGlobalScope::AddConsoleMessage(
-    ConsoleMessage* console_message) {
-  GetFrame()->Console().AddMessage(console_message);
-}
-
-void MainThreadWorkletGlobalScope::ExceptionThrown(ErrorEvent* event) {
-  MainThreadDebugger::Instance()->ExceptionThrown(this, event);
-}
-
-CoreProbeSink* MainThreadWorkletGlobalScope::GetProbeSink() {
-  return probe::ToCoreProbeSink(GetFrame());
-}
-
-void MainThreadWorkletGlobalScope::Trace(blink::Visitor* visitor) {
-  WorkletGlobalScope::Trace(visitor);
-  ContextClient::Trace(visitor);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h b/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h
deleted file mode 100644
index 2b78ae1..0000000
--- a/third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h
+++ /dev/null
@@ -1,53 +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 THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_MAIN_THREAD_WORKLET_GLOBAL_SCOPE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_MAIN_THREAD_WORKLET_GLOBAL_SCOPE_H_
-
-#include "base/single_thread_task_runner.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
-
-namespace blink {
-
-class ConsoleMessage;
-class LocalFrame;
-class WorkerReportingProxy;
-
-class CORE_EXPORT MainThreadWorkletGlobalScope
-    : public WorkletGlobalScope,
-      public ContextClient {
-  USING_GARBAGE_COLLECTED_MIXIN(MainThreadWorkletGlobalScope);
-
- public:
-  MainThreadWorkletGlobalScope(LocalFrame*,
-                               std::unique_ptr<GlobalScopeCreationParams>,
-                               WorkerReportingProxy&);
-  ~MainThreadWorkletGlobalScope() override;
-
-  bool IsMainThreadWorkletGlobalScope() const final { return true; }
-
-  // WorkerOrWorkletGlobalScope
-  WorkerThread* GetThread() const final;
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
-
-  // ExecutionContext
-  void AddConsoleMessage(ConsoleMessage*) final;
-  void ExceptionThrown(ErrorEvent*) final;
-  CoreProbeSink* GetProbeSink() final;
-
-  void Trace(blink::Visitor*) override;
-};
-
-DEFINE_TYPE_CASTS(MainThreadWorkletGlobalScope,
-                  ExecutionContext,
-                  context,
-                  context->IsMainThreadWorkletGlobalScope(),
-                  context.IsMainThreadWorkletGlobalScope());
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_MAIN_THREAD_WORKLET_GLOBAL_SCOPE_H_
diff --git a/third_party/blink/renderer/core/workers/main_thread_worklet_test.cc b/third_party/blink/renderer/core/workers/main_thread_worklet_test.cc
index 9393910..6771bef6 100644
--- a/third_party/blink/renderer/core/workers/main_thread_worklet_test.cc
+++ b/third_party/blink/renderer/core/workers/main_thread_worklet_test.cc
@@ -11,8 +11,8 @@
 #include "third_party/blink/renderer/core/script/script.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/main_thread_worklet_reporting_proxy.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
@@ -72,15 +72,17 @@
         OriginTrialContext::GetTokens(document).get(),
         base::UnguessableToken::Create(), nullptr /* worker_settings */,
         kV8CacheOptionsDefault, new WorkletModuleResponsesMap);
-    global_scope_ = new MainThreadWorkletGlobalScope(
-        &GetFrame(), std::move(creation_params), *reporting_proxy_);
+    global_scope_ = new WorkletGlobalScope(std::move(creation_params),
+                                           *reporting_proxy_, &GetFrame());
+    EXPECT_TRUE(global_scope_->IsMainThreadWorkletGlobalScope());
+    EXPECT_FALSE(global_scope_->IsThreadedWorkletGlobalScope());
   }
 
   void TearDown() override { global_scope_->Dispose(); }
 
  protected:
   std::unique_ptr<MainThreadWorkletReportingProxyForTest> reporting_proxy_;
-  Persistent<MainThreadWorkletGlobalScope> global_scope_;
+  Persistent<WorkletGlobalScope> global_scope_;
 };
 
 class MainThreadWorkletInvalidCSPTest : public MainThreadWorkletTest {
@@ -117,7 +119,7 @@
   // This feature is randomly selected.
   const WebFeature kFeature1 = WebFeature::kRequestFileSystem;
 
-  // API use on the MainThreadWorkletGlobalScope should be recorded in
+  // API use on WorkletGlobalScope for the main thread should be recorded in
   // UseCounter on the Document.
   EXPECT_FALSE(UseCounter::IsCounted(GetDocument(), kFeature1));
   UseCounter::Count(global_scope_, kFeature1);
@@ -130,8 +132,8 @@
   // This feature is randomly selected from Deprecation::deprecationMessage().
   const WebFeature kFeature2 = WebFeature::kPrefixedStorageInfo;
 
-  // Deprecated API use on the MainThreadWorkletGlobalScope should be recorded
-  // in UseCounter on the Document.
+  // Deprecated API use on WorkletGlobalScope for the main thread should be
+  // recorded in UseCounter on the Document.
   EXPECT_FALSE(UseCounter::IsCounted(GetDocument(), kFeature2));
   Deprecation::CountDeprecation(global_scope_, kFeature2);
   EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature2));
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.cc
deleted file mode 100644
index 41f9b198..0000000
--- a/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.cc
+++ /dev/null
@@ -1,63 +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.
-
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
-
-#include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/core/inspector/console_message_storage.h"
-#include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
-#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-#include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
-#include "third_party/blink/renderer/core/workers/worker_thread.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/wtf/assertions.h"
-
-namespace blink {
-
-ThreadedWorkletGlobalScope::ThreadedWorkletGlobalScope(
-    std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    v8::Isolate* isolate,
-    WorkerThread* thread)
-    : WorkletGlobalScope(std::move(creation_params),
-                         isolate,
-                         thread->GetWorkerReportingProxy()),
-      thread_(thread) {
-  BindContentSecurityPolicyToExecutionContext();
-}
-
-ThreadedWorkletGlobalScope::~ThreadedWorkletGlobalScope() {
-  DCHECK(!thread_);
-}
-
-void ThreadedWorkletGlobalScope::Dispose() {
-  DCHECK(IsContextThread());
-  WorkletGlobalScope::Dispose();
-  thread_ = nullptr;
-}
-
-bool ThreadedWorkletGlobalScope::IsContextThread() const {
-  return GetThread()->IsCurrentThread();
-}
-
-void ThreadedWorkletGlobalScope::AddConsoleMessage(
-    ConsoleMessage* console_message) {
-  DCHECK(IsContextThread());
-  GetThread()->GetWorkerReportingProxy().ReportConsoleMessage(
-      console_message->Source(), console_message->Level(),
-      console_message->Message(), console_message->Location());
-  GetThread()->GetConsoleMessageStorage()->AddConsoleMessage(this,
-                                                             console_message);
-}
-
-void ThreadedWorkletGlobalScope::ExceptionThrown(ErrorEvent* error_event) {
-  DCHECK(IsContextThread());
-  if (WorkerThreadDebugger* debugger =
-          WorkerThreadDebugger::From(GetThread()->GetIsolate()))
-    debugger->ExceptionThrown(thread_, error_event);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h b/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h
deleted file mode 100644
index 1755998..0000000
--- a/third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h
+++ /dev/null
@@ -1,48 +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 THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_THREADED_WORKLET_GLOBAL_SCOPE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_THREADED_WORKLET_GLOBAL_SCOPE_H_
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
-
-namespace blink {
-
-class WorkerThread;
-struct GlobalScopeCreationParams;
-
-class CORE_EXPORT ThreadedWorkletGlobalScope : public WorkletGlobalScope {
- public:
-  ~ThreadedWorkletGlobalScope() override;
-  void Dispose() override;
-
-  // ExecutionContext
-  bool IsThreadedWorkletGlobalScope() const final { return true; }
-  bool IsContextThread() const final;
-  void AddConsoleMessage(ConsoleMessage*) final;
-  void ExceptionThrown(ErrorEvent*) final;
-
-  WorkerThread* GetThread() const override { return thread_; }
-
- protected:
-  ThreadedWorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
-                             v8::Isolate*,
-                             WorkerThread*);
-
- private:
-  friend class ThreadedWorkletThreadForTest;
-
-  WorkerThread* thread_;
-};
-
-DEFINE_TYPE_CASTS(ThreadedWorkletGlobalScope,
-                  ExecutionContext,
-                  context,
-                  context->IsThreadedWorkletGlobalScope(),
-                  context.IsThreadedWorkletGlobalScope());
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_THREADED_WORKLET_GLOBAL_SCOPE_H_
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.cc b/third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.cc
index 7dbe518..b98c1b5f 100644
--- a/third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.cc
+++ b/third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.cc
@@ -8,9 +8,9 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 
 namespace blink {
@@ -33,8 +33,8 @@
     scoped_refptr<base::SingleThreadTaskRunner> outside_settings_task_runner,
     WorkletPendingTasks* pending_tasks,
     WorkerThread* worker_thread) {
-  ThreadedWorkletGlobalScope* global_scope =
-      ToThreadedWorkletGlobalScope(worker_thread->GlobalScope());
+  WorkletGlobalScope* global_scope =
+      ToWorkletGlobalScope(worker_thread->GlobalScope());
   global_scope->FetchAndInvokeScript(
       module_url_record, credentials_mode,
       new FetchClientSettingsObjectSnapshot(std::move(outside_settings_object)),
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_test.cc b/third_party/blink/renderer/core/workers/threaded_worklet_test.cc
index abb4481..4b9d485 100644
--- a/third_party/blink/renderer/core/workers/threaded_worklet_test.cc
+++ b/third_party/blink/renderer/core/workers/threaded_worklet_test.cc
@@ -12,11 +12,11 @@
 #include "third_party/blink/renderer/core/script/script.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.h"
 #include "third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_thread_test_helper.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
 #include "third_party/blink/renderer/core/workers/worklet_thread_holder.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
@@ -133,7 +133,7 @@
         FROM_HERE, CrossThreadBind(&test::ExitRunLoop));
   }
 
-  // Emulates API use on ThreadedWorkletGlobalScope.
+  // Emulates API use on threaded WorkletGlobalScope.
   void CountFeature(WebFeature feature) {
     EXPECT_TRUE(IsCurrentThread());
     GlobalScope()->CountFeature(feature);
@@ -142,7 +142,7 @@
         FROM_HERE, CrossThreadBind(&test::ExitRunLoop));
   }
 
-  // Emulates deprecated API use on ThreadedWorkletGlobalScope.
+  // Emulates deprecated API use on threaded WorkletGlobalScope.
   void CountDeprecation(WebFeature feature) {
     EXPECT_TRUE(IsCurrentThread());
     GlobalScope()->CountDeprecation(feature);
@@ -170,8 +170,11 @@
  private:
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams> creation_params) final {
-    return new ThreadedWorkletGlobalScope(std::move(creation_params),
-                                          GetIsolate(), this);
+    auto* global_scope = new WorkletGlobalScope(
+        std::move(creation_params), GetWorkerReportingProxy(), this);
+    EXPECT_FALSE(global_scope->IsMainThreadWorkletGlobalScope());
+    EXPECT_TRUE(global_scope->IsThreadedWorkletGlobalScope());
+    return global_scope;
   }
 
   bool IsOwningBackingThread() const final { return false; }
@@ -306,7 +309,7 @@
   // This feature is randomly selected.
   const WebFeature kFeature1 = WebFeature::kRequestFileSystem;
 
-  // API use on the ThreadedWorkletGlobalScope should be recorded in UseCounter
+  // API use on the threaded WorkletGlobalScope should be recorded in UseCounter
   // on the Document.
   EXPECT_FALSE(UseCounter::IsCounted(GetDocument(), kFeature1));
   PostCrossThreadTask(
@@ -317,7 +320,7 @@
   EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature1));
 
   // API use should be reported to the Document only one time. See comments in
-  // ThreadedWorkletGlobalScopeForTest::CountFeature.
+  // ThreadedWorkletObjectProxyForTest::CountFeature.
   PostCrossThreadTask(
       *GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
       CrossThreadBind(&ThreadedWorkletThreadForTest::CountFeature,
@@ -327,7 +330,7 @@
   // This feature is randomly selected from Deprecation::deprecationMessage().
   const WebFeature kFeature2 = WebFeature::kPrefixedStorageInfo;
 
-  // Deprecated API use on the ThreadedWorkletGlobalScope should be recorded in
+  // Deprecated API use on the threaded WorkletGlobalScope should be recorded in
   // UseCounter on the Document.
   EXPECT_FALSE(UseCounter::IsCounted(GetDocument(), kFeature2));
   PostCrossThreadTask(
@@ -338,7 +341,7 @@
   EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature2));
 
   // API use should be reported to the Document only one time. See comments in
-  // ThreadedWorkletGlobalScopeForTest::CountDeprecation.
+  // ThreadedWorkletObjectProxyForTest::CountDeprecation.
   PostCrossThreadTask(
       *GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
       CrossThreadBind(&ThreadedWorkletThreadForTest::CountDeprecation,
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index b4fa359..5a0ccabb 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/core/loader/modulescript/module_script_fetch_request.h"
 #include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index db87f55d..afc993d 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -83,7 +83,7 @@
   void CountDeprecation(WebFeature);
 
   // May return nullptr if this global scope is not threaded (i.e.,
-  // MainThreadWorkletGlobalScope) or after dispose() is called.
+  // WorkletGlobalScope for the main thread) or after Dispose() is called.
   virtual WorkerThread* GetThread() const = 0;
 
   ResourceFetcher* Fetcher() const override;
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 37d2c551..a231d07 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -40,7 +40,6 @@
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index aea0f978..fb879e4 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -9,12 +9,18 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
+#include "third_party/blink/renderer/core/frame/frame_console.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/inspector/console_message_storage.h"
 #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
+#include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/script/modulator.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
+#include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
 #include "third_party/blink/renderer/core/workers/worklet_module_tree_client.h"
 #include "third_party/blink/renderer/core/workers/worklet_pending_tasks.h"
@@ -23,13 +29,38 @@
 
 namespace blink {
 
+WorkletGlobalScope::WorkletGlobalScope(
+    std::unique_ptr<GlobalScopeCreationParams> creation_params,
+    WorkerReportingProxy& reporting_proxy,
+    LocalFrame* frame)
+    : WorkletGlobalScope(std::move(creation_params),
+                         reporting_proxy,
+                         ToIsolate(frame),
+                         ThreadType::kMainThread,
+                         frame,
+                         nullptr /* worker_thread */) {}
+
+WorkletGlobalScope::WorkletGlobalScope(
+    std::unique_ptr<GlobalScopeCreationParams> creation_params,
+    WorkerReportingProxy& reporting_proxy,
+    WorkerThread* worker_thread)
+    : WorkletGlobalScope(std::move(creation_params),
+                         reporting_proxy,
+                         worker_thread->GetIsolate(),
+                         ThreadType::kOffMainThread,
+                         nullptr /* frame */,
+                         worker_thread) {}
+
 // Partial implementation of the "set up a worklet environment settings object"
 // algorithm:
 // https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
 WorkletGlobalScope::WorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
+    WorkerReportingProxy& reporting_proxy,
     v8::Isolate* isolate,
-    WorkerReportingProxy& reporting_proxy)
+    ThreadType thread_type,
+    LocalFrame* frame,
+    WorkerThread* worker_thread)
     : WorkerOrWorkletGlobalScope(isolate,
                                  creation_params->worker_clients,
                                  reporting_proxy),
@@ -42,7 +73,13 @@
       https_state_(creation_params->starter_https_state),
       agent_cluster_id_(creation_params->agent_cluster_id.is_empty()
                             ? base::UnguessableToken::Create()
-                            : creation_params->agent_cluster_id) {
+                            : creation_params->agent_cluster_id),
+      thread_type_(thread_type),
+      frame_(frame),
+      worker_thread_(worker_thread) {
+  DCHECK((thread_type_ == ThreadType::kMainThread && frame_) ||
+         (thread_type_ == ThreadType::kOffMainThread && worker_thread_));
+
   // Step 2: "Let inheritedAPIBaseURL be outsideSettings's API base URL."
   // |url_| is the inheritedAPIBaseURL passed from the parent Document.
 
@@ -57,6 +94,7 @@
   // workletGlobalScope."
   InitContentSecurityPolicyFromVector(
       creation_params->content_security_policy_parsed_headers);
+  BindContentSecurityPolicyToExecutionContext();
 
   OriginTrialContext::AddTokens(this,
                                 creation_params->origin_trial_tokens.get());
@@ -64,6 +102,14 @@
 
 WorkletGlobalScope::~WorkletGlobalScope() = default;
 
+bool WorkletGlobalScope::IsMainThreadWorkletGlobalScope() const {
+  return thread_type_ == ThreadType::kMainThread;
+}
+
+bool WorkletGlobalScope::IsThreadedWorkletGlobalScope() const {
+  return thread_type_ == ThreadType::kOffMainThread;
+}
+
 ExecutionContext* WorkletGlobalScope::GetExecutionContext() const {
   return const_cast<WorkletGlobalScope*>(this);
 }
@@ -78,6 +124,64 @@
   return false;
 }
 
+bool WorkletGlobalScope::IsContextThread() const {
+  if (IsMainThreadWorkletGlobalScope())
+    return IsMainThread();
+  return worker_thread_->IsCurrentThread();
+}
+
+void WorkletGlobalScope::AddConsoleMessage(ConsoleMessage* console_message) {
+  if (IsMainThreadWorkletGlobalScope()) {
+    frame_->Console().AddMessage(console_message);
+    return;
+  }
+  worker_thread_->GetWorkerReportingProxy().ReportConsoleMessage(
+      console_message->Source(), console_message->Level(),
+      console_message->Message(), console_message->Location());
+  worker_thread_->GetConsoleMessageStorage()->AddConsoleMessage(
+      worker_thread_->GlobalScope(), console_message);
+}
+
+void WorkletGlobalScope::ExceptionThrown(ErrorEvent* error_event) {
+  if (IsMainThreadWorkletGlobalScope()) {
+    MainThreadDebugger::Instance()->ExceptionThrown(this, error_event);
+    return;
+  }
+  if (WorkerThreadDebugger* debugger =
+          WorkerThreadDebugger::From(GetThread()->GetIsolate())) {
+    debugger->ExceptionThrown(worker_thread_, error_event);
+  }
+}
+
+void WorkletGlobalScope::Dispose() {
+  frame_ = nullptr;
+  worker_thread_ = nullptr;
+  WorkerOrWorkletGlobalScope::Dispose();
+}
+
+WorkerThread* WorkletGlobalScope::GetThread() const {
+  DCHECK(!IsMainThreadWorkletGlobalScope());
+  return worker_thread_;
+}
+
+CoreProbeSink* WorkletGlobalScope::GetProbeSink() {
+  if (IsMainThreadWorkletGlobalScope())
+    return probe::ToCoreProbeSink(frame_);
+  return nullptr;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> WorkletGlobalScope::GetTaskRunner(
+    TaskType task_type) {
+  if (IsMainThreadWorkletGlobalScope())
+    return frame_->GetFrameScheduler()->GetTaskRunner(task_type);
+  return worker_thread_->GetTaskRunner(task_type);
+}
+
+LocalFrame* WorkletGlobalScope::GetFrame() const {
+  DCHECK(IsMainThreadWorkletGlobalScope());
+  return frame_;
+}
+
 // Implementation of the first half of the "fetch and invoke a worklet script"
 // algorithm:
 // https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
@@ -135,6 +239,7 @@
 }
 
 void WorkletGlobalScope::Trace(blink::Visitor* visitor) {
+  visitor->Trace(frame_);
   WorkerOrWorkletGlobalScope::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
index bc94455..1bdbef8 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
@@ -25,6 +25,13 @@
 class WorkerReportingProxy;
 struct GlobalScopeCreationParams;
 
+// This is an implementation of the web-exposed WorkletGlobalScope interface
+// defined in the Worklets spec:
+// https://drafts.css-houdini.org/worklets/#workletglobalscope
+//
+// This instance lives either on the main thread (main thread worklet) or a
+// worker thread (threaded worklet). It's determined by constructors. See
+// comments on the constructors.
 class CORE_EXPORT WorkletGlobalScope
     : public WorkerOrWorkletGlobalScope,
       public ActiveScriptWrappable<WorkletGlobalScope> {
@@ -34,6 +41,8 @@
  public:
   ~WorkletGlobalScope() override;
 
+  bool IsMainThreadWorkletGlobalScope() const final;
+  bool IsThreadedWorkletGlobalScope() const final;
   bool IsWorkletGlobalScope() const final { return true; }
 
   // Always returns false here as PaintWorkletGlobalScope and
@@ -50,6 +59,18 @@
   String UserAgent() const final { return user_agent_; }
   SecurityContext& GetSecurityContext() final { return *this; }
   bool IsSecureContext(String& error_message) const final;
+  bool IsContextThread() const final;
+  void AddConsoleMessage(ConsoleMessage*) final;
+  void ExceptionThrown(ErrorEvent*) final;
+  CoreProbeSink* GetProbeSink() final;
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) final;
+
+  // WorkerOrWorkletGlobalScope
+  void Dispose() override;
+  WorkerThread* GetThread() const final;
+
+  virtual LocalFrame* GetFrame() const;
+
   const base::UnguessableToken& GetAgentClusterID() const final {
     // Currently, worklet agents have no clearly defined owner. See
     // https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-agent-cluster-formalism
@@ -101,19 +122,40 @@
 
   HttpsState GetHttpsState() const override { return https_state_; }
 
- protected:
-  // Partial implementation of the "set up a worklet environment settings
-  // object" algorithm:
-  // https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
+  // Constructs an instance as a main thread worklet. Must be called on the main
+  // thread.
   WorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
-                     v8::Isolate*,
-                     WorkerReportingProxy&);
-
-  void BindContentSecurityPolicyToExecutionContext() override;
+                     WorkerReportingProxy&,
+                     LocalFrame*);
+  // Constructs an instance as a threaded worklet. Must be called on a worker
+  // thread.
+  WorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
+                     WorkerReportingProxy&,
+                     WorkerThread*);
 
  private:
+  enum class ThreadType {
+    // Indicates this global scope lives on the main thread.
+    kMainThread,
+    // Indicates this global scope lives on a worker thread.
+    kOffMainThread
+  };
+
+  // The base constructor delegated from other public constructors. This
+  // partially implements the "set up a worklet environment settings object"
+  // algorithm defined in the Worklets spec:
+  // https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
+  WorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
+                     WorkerReportingProxy&,
+                     v8::Isolate*,
+                     ThreadType,
+                     LocalFrame*,
+                     WorkerThread*);
+
   EventTarget* ErrorEventTarget() final { return nullptr; }
 
+  void BindContentSecurityPolicyToExecutionContext() override;
+
   // The |url_| and |user_agent_| are inherited from the parent Document.
   const KURL url_;
   const String user_agent_;
@@ -130,6 +172,12 @@
   const HttpsState https_state_;
 
   const base::UnguessableToken agent_cluster_id_;
+
+  const ThreadType thread_type_;
+  // |frame_| is available only when |thread_type_| is kMainThread.
+  Member<LocalFrame> frame_;
+  // |worker_thread_| is available only when |thread_type_| is kOffMainThread.
+  WorkerThread* worker_thread_;
 };
 
 DEFINE_TYPE_CASTS(WorkletGlobalScope,
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index 3becaaf..c7912c9 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_parser.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
+#include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h"
 #include "third_party/blink/renderer/modules/animationworklet/worklet_animation_options.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -35,25 +36,23 @@
 
 AnimationWorkletGlobalScope* AnimationWorkletGlobalScope::Create(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    v8::Isolate* isolate,
     WorkerThread* thread) {
-  return new AnimationWorkletGlobalScope(std::move(creation_params), isolate,
-                                         thread);
+  return new AnimationWorkletGlobalScope(std::move(creation_params), thread);
 }
 
 AnimationWorkletGlobalScope::AnimationWorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    v8::Isolate* isolate,
     WorkerThread* thread)
-    : ThreadedWorkletGlobalScope(std::move(creation_params), isolate, thread) {
-}
+    : WorkletGlobalScope(std::move(creation_params),
+                         thread->GetWorkerReportingProxy(),
+                         thread) {}
 
 AnimationWorkletGlobalScope::~AnimationWorkletGlobalScope() = default;
 
 void AnimationWorkletGlobalScope::Trace(blink::Visitor* visitor) {
   visitor->Trace(animator_definitions_);
   visitor->Trace(animators_);
-  ThreadedWorkletGlobalScope::Trace(visitor);
+  WorkletGlobalScope::Trace(visitor);
 }
 
 void AnimationWorkletGlobalScope::Dispose() {
@@ -61,7 +60,7 @@
   if (AnimationWorkletProxyClient* proxy_client =
           AnimationWorkletProxyClient::From(Clients()))
     proxy_client->Dispose();
-  ThreadedWorkletGlobalScope::Dispose();
+  WorkletGlobalScope::Dispose();
 }
 
 Animator* AnimationWorkletGlobalScope::CreateAnimatorFor(
@@ -199,9 +198,11 @@
   if (options && options->GetData())
     value = options->GetData()->Deserialize(isolate);
 
-  v8::Local<v8::Object> instance;
-  if (!V8ObjectConstructor::NewInstance(isolate, constructor,
-                                        !value.IsEmpty() ? 1 : 0, &value)
+  v8::Local<v8::Value> instance;
+  if (!V8ScriptRunner::CallAsConstructor(
+           isolate, constructor,
+           ExecutionContext::From(ScriptController()->GetScriptState()),
+           !value.IsEmpty() ? 1 : 0, &value)
            .ToLocal(&instance))
     return nullptr;
 
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
index 3c117e8..c0ee79b 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_ANIMATION_WORKLET_GLOBAL_SCOPE_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/animationworklet/animator.h"
 #include "third_party/blink/renderer/modules/animationworklet/animator_definition.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -27,14 +27,12 @@
 // The scope keeps a map of these animator definitions and can look them up
 // based on their name. The scope also owns a list of active animators that it
 // animates.
-class MODULES_EXPORT AnimationWorkletGlobalScope
-    : public ThreadedWorkletGlobalScope {
+class MODULES_EXPORT AnimationWorkletGlobalScope : public WorkletGlobalScope {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
   static AnimationWorkletGlobalScope* Create(
       std::unique_ptr<GlobalScopeCreationParams>,
-      v8::Isolate*,
       WorkerThread*);
   ~AnimationWorkletGlobalScope() override;
   void Trace(blink::Visitor*) override;
@@ -54,7 +52,6 @@
 
  private:
   AnimationWorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
-                              v8::Isolate*,
                               WorkerThread*);
 
   void RegisterWithProxyClientIfNeeded();
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.cc
index d679d4d..6f047664 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.cc
@@ -66,8 +66,7 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("animation-worklet"),
                "AnimationWorkletThread::CreateWorkerGlobalScope");
-  return AnimationWorkletGlobalScope::Create(std::move(creation_params),
-                                             GetIsolate(), this);
+  return AnimationWorkletGlobalScope::Create(std::move(creation_params), this);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.cc b/third_party/blink/renderer/modules/animationworklet/animator.cc
index 8c87bd0..5faecda3 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animator.cc
@@ -15,7 +15,7 @@
 
 Animator::Animator(v8::Isolate* isolate,
                    AnimatorDefinition* definition,
-                   v8::Local<v8::Object> instance)
+                   v8::Local<v8::Value> instance)
     : definition_(definition),
       instance_(isolate, instance),
       effect_(new EffectProxy()) {}
@@ -25,7 +25,7 @@
 void Animator::Trace(blink::Visitor* visitor) {
   visitor->Trace(definition_);
   visitor->Trace(effect_);
-  visitor->Trace(instance_.Cast<v8::Value>());
+  visitor->Trace(instance_);
 }
 
 bool Animator::Animate(
@@ -34,7 +34,7 @@
     AnimationWorkletDispatcherOutput::AnimationState* output) {
   v8::Isolate* isolate = script_state->GetIsolate();
 
-  v8::Local<v8::Object> instance = instance_.NewLocal(isolate);
+  v8::Local<v8::Value> instance = instance_.NewLocal(isolate);
   v8::Local<v8::Function> animate = definition_->AnimateLocal(isolate);
 
   if (IsUndefinedOrNull(instance) || IsUndefinedOrNull(animate))
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.h b/third_party/blink/renderer/modules/animationworklet/animator.h
index ed8197b..c37bf14d 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.h
+++ b/third_party/blink/renderer/modules/animationworklet/animator.h
@@ -25,7 +25,7 @@
 class Animator final : public GarbageCollectedFinalized<Animator>,
                        public NameClient {
  public:
-  Animator(v8::Isolate*, AnimatorDefinition*, v8::Local<v8::Object> instance);
+  Animator(v8::Isolate*, AnimatorDefinition*, v8::Local<v8::Value> instance);
   ~Animator();
   void Trace(blink::Visitor*);
   const char* NameInHeapSnapshot() const override { return "Animator"; }
@@ -44,7 +44,7 @@
   // This object keeps the definition object, and animator instance alive.
   // It participates in wrapper tracing as it holds onto V8 wrappers.
   TraceWrapperMember<AnimatorDefinition> definition_;
-  TraceWrapperV8Reference<v8::Object> instance_;
+  TraceWrapperV8Reference<v8::Value> instance_;
 
   Member<EffectProxy> effect_;
 };
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_event.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_event.idl
index e4c633e..65c470a 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_event.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_event.idl
@@ -7,7 +7,7 @@
 [
     Constructor(DOMString type, BackgroundFetchEventInit init),
     Exposed=ServiceWorker,
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchEvent : ExtendableEvent {
     readonly attribute BackgroundFetchRegistration registration;
 };
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_fetch.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_fetch.idl
index c87e156..df03e49 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_fetch.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_fetch.idl
@@ -6,7 +6,7 @@
 
 [
     Exposed=(Window,Worker),
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchFetch {
     readonly attribute Request request;
 };
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.idl
index d62ceda..cd401cc 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.idl
@@ -6,7 +6,7 @@
 
 [
     Exposed=(Window,Worker),
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchManager {
     [CallWith=ScriptState, RaisesException, MeasureAs=BackgroundFetchManagerFetch] Promise<BackgroundFetchRegistration> fetch(DOMString id, (RequestInfo or sequence<RequestInfo>) requests, optional BackgroundFetchOptions options);
     [CallWith=ScriptState, MeasureAs=BackgroundFetchManagerGet] Promise<BackgroundFetchRegistration?> get(DOMString id);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_record.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_record.idl
index a3e4080..bf81238 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_record.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_record.idl
@@ -6,7 +6,7 @@
 
 [
     Exposed=(ServiceWorker),
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchRecord {
     readonly attribute Request request;
     [CallWith=ScriptState] readonly attribute Promise<Response> responseReady;
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
index 819479c..6b102bb5 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
@@ -74,14 +74,19 @@
       ->AddRegistrationObserver(unique_id_, std::move(observer));
 }
 
-void BackgroundFetchRegistration::OnProgress(uint64_t upload_total,
-                                             uint64_t uploaded,
-                                             uint64_t download_total,
-                                             uint64_t downloaded) {
+void BackgroundFetchRegistration::OnProgress(
+    uint64_t upload_total,
+    uint64_t uploaded,
+    uint64_t download_total,
+    uint64_t downloaded,
+    mojom::BackgroundFetchResult result,
+    mojom::BackgroundFetchFailureReason failure_reason) {
   upload_total_ = upload_total;
   uploaded_ = uploaded;
   download_total_ = download_total;
   downloaded_ = downloaded;
+  result_ = result;
+  failure_reason_ = failure_reason;
 
   ExecutionContext* context = GetExecutionContext();
   if (!context || context->IsContextDestroyed())
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
index b39e712..bb2adb7c 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
@@ -57,7 +57,9 @@
   void OnProgress(uint64_t upload_total,
                   uint64_t uploaded,
                   uint64_t download_total,
-                  uint64_t downloaded) override;
+                  uint64_t downloaded,
+                  mojom::BackgroundFetchResult result,
+                  mojom::BackgroundFetchFailureReason failure_reason) override;
   void OnRecordsUnavailable() override;
 
   // Web Exposed attribute defined in the IDL file. Corresponds to the
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.idl
index e277b8e..5760956f 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.idl
@@ -22,7 +22,7 @@
 
 [
     Exposed=(Window,Worker),
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchRegistration : EventTarget {
     readonly attribute DOMString id;
     readonly attribute unsigned long long uploadTotal;
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_settled_fetch.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_settled_fetch.idl
index 74090be..8bee778 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_settled_fetch.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_settled_fetch.idl
@@ -7,7 +7,7 @@
 [
     Constructor(Request request, Response response),
     Exposed=ServiceWorker,
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchSettledFetch : BackgroundFetchFetch {
     readonly attribute Response? response;
 };
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.idl b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.idl
index b494c006..20a3655 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.idl
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.idl
@@ -7,7 +7,7 @@
 [
     Constructor(DOMString type, BackgroundFetchEventInit init),
     Exposed=ServiceWorker,
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] interface BackgroundFetchUpdateUIEvent : BackgroundFetchEvent {
     [CallWith=ScriptState] Promise<void> updateUI(BackgroundFetchUIOptions options);
 };
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/background_fetch/service_worker_global_scope_background_fetch.idl b/third_party/blink/renderer/modules/background_fetch/service_worker_global_scope_background_fetch.idl
index 2f9f6c0..7dc81fe2 100644
--- a/third_party/blink/renderer/modules/background_fetch/service_worker_global_scope_background_fetch.idl
+++ b/third_party/blink/renderer/modules/background_fetch/service_worker_global_scope_background_fetch.idl
@@ -6,7 +6,7 @@
 
 [
     ImplementedAs=ServiceWorkerGlobalScopeBackgroundFetch,
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] partial interface ServiceWorkerGlobalScope {
     attribute EventHandler onbackgroundfetchsuccess;
     attribute EventHandler onbackgroundfetchfail;
diff --git a/third_party/blink/renderer/modules/background_fetch/service_worker_registration_background_fetch.idl b/third_party/blink/renderer/modules/background_fetch/service_worker_registration_background_fetch.idl
index a1d9dee7..529646b 100644
--- a/third_party/blink/renderer/modules/background_fetch/service_worker_registration_background_fetch.idl
+++ b/third_party/blink/renderer/modules/background_fetch/service_worker_registration_background_fetch.idl
@@ -7,7 +7,7 @@
 [
     Exposed=(Window,Worker),
     ImplementedAs=ServiceWorkerRegistrationBackgroundFetch,
-    RuntimeEnabled=BackgroundFetch
+    OriginTrialEnabled=BackgroundFetch
 ] partial interface ServiceWorkerRegistration {
     readonly attribute BackgroundFetchManager backgroundFetch;
 };
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
index 4ab1a29..ffd88e09 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
@@ -80,7 +80,7 @@
   MaybeCreatePaintInstance();
 
   v8::Isolate* isolate = script_state_->GetIsolate();
-  v8::Local<v8::Object> instance = instance_.NewLocal(isolate);
+  v8::Local<v8::Value> instance = instance_.NewLocal(isolate);
 
   // We may have failed to create an instance class, in which case produce an
   // invalid image.
@@ -144,11 +144,11 @@
   v8::Local<v8::Function> constructor = constructor_.NewLocal(isolate);
   DCHECK(!IsUndefinedOrNull(constructor));
 
-  v8::Local<v8::Object> paint_instance;
-  if (V8ObjectConstructor::NewInstance(isolate, constructor)
-          .ToLocal(&paint_instance)) {
+  v8::Local<v8::Value> paint_instance;
+  if (V8ScriptRunner::CallAsConstructor(
+          isolate, constructor, ExecutionContext::From(script_state_), 0, {})
+          .ToLocal(&paint_instance))
     instance_.Set(isolate, paint_instance);
-  }
 
   did_call_constructor_ = true;
 }
@@ -156,7 +156,7 @@
 void CSSPaintDefinition::Trace(Visitor* visitor) {
   visitor->Trace(constructor_.Cast<v8::Value>());
   visitor->Trace(paint_.Cast<v8::Value>());
-  visitor->Trace(instance_.Cast<v8::Value>());
+  visitor->Trace(instance_);
   visitor->Trace(script_state_);
 }
 
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
index dede4c7..4622a7b 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
@@ -97,7 +97,7 @@
   TraceWrapperV8Reference<v8::Function> paint_;
 
   // At the moment there is only ever one instance of a paint class per type.
-  TraceWrapperV8Reference<v8::Object> instance_;
+  TraceWrapperV8Reference<v8::Value> instance_;
 
   bool did_call_constructor_;
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
index 69116af..7e2bbcf 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -111,9 +111,7 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerReportingProxy& reporting_proxy,
     PaintWorkletPendingGeneratorRegistry* pending_generator_registry)
-    : MainThreadWorkletGlobalScope(frame,
-                                   std::move(creation_params),
-                                   reporting_proxy),
+    : WorkletGlobalScope(std::move(creation_params), reporting_proxy, frame),
       pending_generator_registry_(pending_generator_registry) {}
 
 PaintWorkletGlobalScope::~PaintWorkletGlobalScope() = default;
@@ -121,9 +119,8 @@
 void PaintWorkletGlobalScope::Dispose() {
   MainThreadDebugger::Instance()->ContextWillBeDestroyed(
       ScriptController()->GetScriptState());
-
   pending_generator_registry_ = nullptr;
-  MainThreadWorkletGlobalScope::Dispose();
+  WorkletGlobalScope::Dispose();
 }
 
 void PaintWorkletGlobalScope::registerPaint(
@@ -230,7 +227,7 @@
 void PaintWorkletGlobalScope::Trace(blink::Visitor* visitor) {
   visitor->Trace(paint_definitions_);
   visitor->Trace(pending_generator_registry_);
-  MainThreadWorkletGlobalScope::Trace(visitor);
+  WorkletGlobalScope::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
index 1817e5df..6bd200d 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/workers/main_thread_worklet_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_pending_generator_registry.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -18,8 +18,7 @@
 class ExceptionState;
 class WorkerReportingProxy;
 
-class MODULES_EXPORT PaintWorkletGlobalScope final
-    : public MainThreadWorkletGlobalScope {
+class MODULES_EXPORT PaintWorkletGlobalScope final : public WorkletGlobalScope {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(PaintWorkletGlobalScope);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index fd3fc68a..1072ca6 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -114,7 +114,9 @@
 #include "third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/webrtc/api/jsep.h"
 #include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/pc/sessiondescription.h"
 
 namespace blink {
 
@@ -552,7 +554,8 @@
   }
 
   RTCPeerConnection* peer_connection = new RTCPeerConnection(
-      context, std::move(configuration), constraints, exception_state);
+      context, std::move(configuration), rtc_configuration.hasSdpSemantics(),
+      constraints, exception_state);
   peer_connection->PauseIfNeeded();
   if (exception_state.HadException())
     return nullptr;
@@ -567,6 +570,7 @@
 RTCPeerConnection::RTCPeerConnection(
     ExecutionContext* context,
     webrtc::PeerConnectionInterface::RTCConfiguration configuration,
+    bool sdp_semantics_specified,
     WebMediaConstraints constraints,
     ExceptionState& exception_state)
     : PausableObject(context),
@@ -585,7 +589,8 @@
       stopped_(false),
       closed_(false),
       has_data_channels_(false),
-      sdp_semantics_(configuration.sdp_semantics) {
+      sdp_semantics_(configuration.sdp_semantics),
+      sdp_semantics_specified_(sdp_semantics_specified) {
   Document* document = To<Document>(GetExecutionContext());
 
   InstanceCounters::IncrementCounter(
@@ -841,9 +846,47 @@
   return nullptr;
 }
 
+bool RTCPeerConnection::ShouldShowComplexPlanBSdpWarning(
+    const RTCSessionDescriptionInit& session_description_init) const {
+  if (sdp_semantics_specified_)
+    return false;
+  if (!session_description_init.hasType() || !session_description_init.hasSdp())
+    return false;
+  std::unique_ptr<webrtc::SessionDescriptionInterface> session_description(
+      webrtc::CreateSessionDescription(
+          session_description_init.type().Utf8().data(),
+          session_description_init.sdp().Utf8().data(), nullptr));
+  if (!session_description)
+    return false;
+  size_t num_audio_mlines = 0u;
+  size_t num_video_mlines = 0u;
+  size_t num_audio_tracks = 0u;
+  size_t num_video_tracks = 0u;
+  for (const cricket::ContentInfo& content :
+       session_description->description()->contents()) {
+    cricket::MediaType media_type = content.media_description()->type();
+    size_t num_tracks = std::max(static_cast<size_t>(1u),
+                                 content.media_description()->streams().size());
+    if (media_type == cricket::MEDIA_TYPE_AUDIO) {
+      ++num_audio_mlines;
+      num_audio_tracks += num_tracks;
+    } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
+      ++num_video_mlines;
+      num_video_tracks += num_tracks;
+    }
+  }
+  return (num_audio_mlines == 1u && num_audio_tracks > 1u) ||
+         (num_video_mlines == 1u && num_video_tracks > 1u);
+}
+
 ScriptPromise RTCPeerConnection::setLocalDescription(
     ScriptState* script_state,
     const RTCSessionDescriptionInit& session_description_init) {
+  if (ShouldShowComplexPlanBSdpWarning(session_description_init)) {
+    Deprecation::CountDeprecation(
+        GetExecutionContext(),
+        WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
+  }
   String sdp;
   DOMException* exception = checkSdpForStateErrors(
       ExecutionContext::From(script_state), session_description_init, &sdp);
@@ -864,6 +907,11 @@
     const RTCSessionDescriptionInit& session_description_init,
     V8VoidFunction* success_callback,
     V8RTCPeerConnectionErrorCallback* error_callback) {
+  if (ShouldShowComplexPlanBSdpWarning(session_description_init)) {
+    Deprecation::CountDeprecation(
+        GetExecutionContext(),
+        WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
+  }
   ExecutionContext* context = ExecutionContext::From(script_state);
   if (success_callback && error_callback) {
     UseCounter::Count(
@@ -929,6 +977,11 @@
 ScriptPromise RTCPeerConnection::setRemoteDescription(
     ScriptState* script_state,
     const RTCSessionDescriptionInit& session_description_init) {
+  if (ShouldShowComplexPlanBSdpWarning(session_description_init)) {
+    Deprecation::CountDeprecation(
+        GetExecutionContext(),
+        WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
+  }
   if (signaling_state_ ==
       webrtc::PeerConnectionInterface::SignalingState::kClosed) {
     return ScriptPromise::RejectWithDOMException(
@@ -951,6 +1004,11 @@
     const RTCSessionDescriptionInit& session_description_init,
     V8VoidFunction* success_callback,
     V8RTCPeerConnectionErrorCallback* error_callback) {
+  if (ShouldShowComplexPlanBSdpWarning(session_description_init)) {
+    Deprecation::CountDeprecation(
+        GetExecutionContext(),
+        WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
+  }
   ExecutionContext* context = ExecutionContext::From(script_state);
   if (success_callback && error_callback) {
     UseCounter::Count(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 49450d4..fc779f3 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -258,6 +258,18 @@
   static int PeerConnectionCount();
   static int PeerConnectionCountLimit();
 
+  // SLD/SRD helper method, public for testing.
+  // "Complex" Plan B SDP is SDP that is not compatible with Unified Plan, i.e.
+  // SDP that has multiple tracks listed under the same m= sections. We should
+  // show a deprecation warning when setLocalDescription() or
+  // setRemoteDescription() is called and:
+  // - The SDP is complex Plan B SDP.
+  // - sdpSemantics was not specified at RTCPeerConnection construction.
+  // Such calls would normally succeed, but as soon as the default switches to
+  // Unified Plan they would fail. This decides whether to show deprecation for
+  // WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics.
+  bool ShouldShowComplexPlanBSdpWarning(const RTCSessionDescriptionInit&) const;
+
   void Trace(blink::Visitor*) override;
 
  private:
@@ -288,6 +300,7 @@
 
   RTCPeerConnection(ExecutionContext*,
                     webrtc::PeerConnectionInterface::RTCConfiguration,
+                    bool sdp_semantics_specified,
                     WebMediaConstraints,
                     ExceptionState&);
   void Dispose();
@@ -442,6 +455,8 @@
   // "kUnifiedPlan", if constructed with "kDefault" it is translated to one or
   // the other.
   webrtc::SdpSemantics sdp_semantics_;
+  // Whether sdpSemantics was specified at construction.
+  bool sdp_semantics_specified_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index cdef143..a20d8fa8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -19,16 +19,352 @@
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_configuration.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_server.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h"
 
 namespace blink {
 
+static const char* kOfferSdpUnifiedPlanSingleAudioSingleVideo =
+    "v=0\r\n"
+    "o=- 6676943034916303038 2 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "t=0 0\r\n"
+    "a=group:BUNDLE 0 1\r\n"
+    "a=msid-semantic: WMS\r\n"
+    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
+    "126\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:pKAt\r\n"
+    "a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
+    "68:BF:5E:7D:01:A3:EC:93\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:0\r\n"
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
+    "a=sendrecv\r\n"
+    "a=msid:- 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtpmap:111 opus/48000/2\r\n"
+    "a=rtcp-fb:111 transport-cc\r\n"
+    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
+    "a=rtpmap:103 ISAC/16000\r\n"
+    "a=rtpmap:104 ISAC/32000\r\n"
+    "a=rtpmap:9 G722/8000\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:106 CN/32000\r\n"
+    "a=rtpmap:105 CN/16000\r\n"
+    "a=rtpmap:13 CN/8000\r\n"
+    "a=rtpmap:110 telephone-event/48000\r\n"
+    "a=rtpmap:112 telephone-event/32000\r\n"
+    "a=rtpmap:113 telephone-event/16000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=ssrc:4264546776 cname:GkUsSfx+DbDplYYT\r\n"
+    "a=ssrc:4264546776 msid: 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
+    "a=ssrc:4264546776 mslabel:\r\n"
+    "a=ssrc:4264546776 label:36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
+    "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:pKAt\r\n"
+    "a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
+    "68:BF:5E:7D:01:A3:EC:93\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:1\r\n"
+    "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
+    "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
+    "a=extmap:4 urn:3gpp:video-orientation\r\n"
+    "a=extmap:5 "
+    "http://www.ietf.org/id/"
+    "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
+    "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
+    "a=extmap:7 "
+    "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
+    "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
+    "a=extmap:10 "
+    "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
+    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
+    "a=sendrecv\r\n"
+    "a=msid:- 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtcp-rsize\r\n"
+    "a=rtpmap:96 VP8/90000\r\n"
+    "a=rtcp-fb:96 goog-remb\r\n"
+    "a=rtcp-fb:96 transport-cc\r\n"
+    "a=rtcp-fb:96 ccm fir\r\n"
+    "a=rtcp-fb:96 nack\r\n"
+    "a=rtcp-fb:96 nack pli\r\n"
+    "a=rtpmap:97 rtx/90000\r\n"
+    "a=fmtp:97 apt=96\r\n"
+    "a=rtpmap:98 VP9/90000\r\n"
+    "a=rtcp-fb:98 goog-remb\r\n"
+    "a=rtcp-fb:98 transport-cc\r\n"
+    "a=rtcp-fb:98 ccm fir\r\n"
+    "a=rtcp-fb:98 nack\r\n"
+    "a=rtcp-fb:98 nack pli\r\n"
+    "a=fmtp:98 x-google-profile-id=0\r\n"
+    "a=rtpmap:99 rtx/90000\r\n"
+    "a=fmtp:99 apt=98\r\n"
+    "a=rtpmap:100 red/90000\r\n"
+    "a=rtpmap:101 rtx/90000\r\n"
+    "a=fmtp:101 apt=100\r\n"
+    "a=rtpmap:102 ulpfec/90000\r\n"
+    "a=ssrc-group:FID 680673332 1566706172\r\n"
+    "a=ssrc:680673332 cname:GkUsSfx+DbDplYYT\r\n"
+    "a=ssrc:680673332 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
+    "a=ssrc:680673332 mslabel:\r\n"
+    "a=ssrc:680673332 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
+    "a=ssrc:1566706172 cname:GkUsSfx+DbDplYYT\r\n"
+    "a=ssrc:1566706172 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
+    "a=ssrc:1566706172 mslabel:\r\n"
+    "a=ssrc:1566706172 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n";
+
+static const char* kOfferSdpUnifiedPlanMultipleAudioTracks =
+    "v=0\r\n"
+    "o=- 1821816752660535838 2 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "t=0 0\r\n"
+    "a=group:BUNDLE 0 1\r\n"
+    "a=msid-semantic: WMS\r\n"
+    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
+    "126\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:rbEc\r\n"
+    "a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
+    "5E:00:A5:27:22:BB:26:5D\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:0\r\n"
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
+    "a=sendrecv\r\n"
+    "a=msid:- adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtpmap:111 opus/48000/2\r\n"
+    "a=rtcp-fb:111 transport-cc\r\n"
+    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
+    "a=rtpmap:103 ISAC/16000\r\n"
+    "a=rtpmap:104 ISAC/32000\r\n"
+    "a=rtpmap:9 G722/8000\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:106 CN/32000\r\n"
+    "a=rtpmap:105 CN/16000\r\n"
+    "a=rtpmap:13 CN/8000\r\n"
+    "a=rtpmap:110 telephone-event/48000\r\n"
+    "a=rtpmap:112 telephone-event/32000\r\n"
+    "a=rtpmap:113 telephone-event/16000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=ssrc:2988156579 cname:gr88KGUzymBvrIaJ\r\n"
+    "a=ssrc:2988156579 msid: adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
+    "a=ssrc:2988156579 mslabel:\r\n"
+    "a=ssrc:2988156579 label:adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
+    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
+    "126\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:rbEc\r\n"
+    "a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
+    "5E:00:A5:27:22:BB:26:5D\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:1\r\n"
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
+    "a=sendrecv\r\n"
+    "a=msid:- b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtpmap:111 opus/48000/2\r\n"
+    "a=rtcp-fb:111 transport-cc\r\n"
+    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
+    "a=rtpmap:103 ISAC/16000\r\n"
+    "a=rtpmap:104 ISAC/32000\r\n"
+    "a=rtpmap:9 G722/8000\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:106 CN/32000\r\n"
+    "a=rtpmap:105 CN/16000\r\n"
+    "a=rtpmap:13 CN/8000\r\n"
+    "a=rtpmap:110 telephone-event/48000\r\n"
+    "a=rtpmap:112 telephone-event/32000\r\n"
+    "a=rtpmap:113 telephone-event/16000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=ssrc:2562757057 cname:gr88KGUzymBvrIaJ\r\n"
+    "a=ssrc:2562757057 msid: b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
+    "a=ssrc:2562757057 mslabel:\r\n"
+    "a=ssrc:2562757057 label:b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n";
+
+static const char* kOfferSdpPlanBSingleAudioSingleVideo =
+    "v=0\r\n"
+    "o=- 267029810971159627 2 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "t=0 0\r\n"
+    "a=group:BUNDLE audio video\r\n"
+    "a=msid-semantic: WMS 655e92b8-9130-44d8-a188-f5f4633d1a8d "
+    "b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
+    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
+    "126\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:ErlQ\r\n"
+    "a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
+    "B0:3A:33:82:C8:67:FF:7A\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:audio\r\n"
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+    "a=sendrecv\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtpmap:111 opus/48000/2\r\n"
+    "a=rtcp-fb:111 transport-cc\r\n"
+    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
+    "a=rtpmap:103 ISAC/16000\r\n"
+    "a=rtpmap:104 ISAC/32000\r\n"
+    "a=rtpmap:9 G722/8000\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:106 CN/32000\r\n"
+    "a=rtpmap:105 CN/16000\r\n"
+    "a=rtpmap:13 CN/8000\r\n"
+    "a=rtpmap:110 telephone-event/48000\r\n"
+    "a=rtpmap:112 telephone-event/32000\r\n"
+    "a=rtpmap:113 telephone-event/16000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=ssrc:1670492497 cname:rNEKgm1NFupmwR4x\r\n"
+    "a=ssrc:1670492497 msid:b15218e5-f921-4988-9e1f-6e50ecbd24c2 "
+    "089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
+    "a=ssrc:1670492497 mslabel:b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
+    "a=ssrc:1670492497 label:089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
+    "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:ErlQ\r\n"
+    "a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
+    "B0:3A:33:82:C8:67:FF:7A\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:video\r\n"
+    "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
+    "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
+    "a=extmap:4 urn:3gpp:video-orientation\r\n"
+    "a=extmap:5 "
+    "http://www.ietf.org/id/"
+    "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
+    "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
+    "a=extmap:7 "
+    "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
+    "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
+    "a=extmap:10 "
+    "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
+    "a=sendrecv\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtcp-rsize\r\n"
+    "a=rtpmap:96 VP8/90000\r\n"
+    "a=rtcp-fb:96 goog-remb\r\n"
+    "a=rtcp-fb:96 transport-cc\r\n"
+    "a=rtcp-fb:96 ccm fir\r\n"
+    "a=rtcp-fb:96 nack\r\n"
+    "a=rtcp-fb:96 nack pli\r\n"
+    "a=rtpmap:97 rtx/90000\r\n"
+    "a=fmtp:97 apt=96\r\n"
+    "a=rtpmap:98 VP9/90000\r\n"
+    "a=rtcp-fb:98 goog-remb\r\n"
+    "a=rtcp-fb:98 transport-cc\r\n"
+    "a=rtcp-fb:98 ccm fir\r\n"
+    "a=rtcp-fb:98 nack\r\n"
+    "a=rtcp-fb:98 nack pli\r\n"
+    "a=fmtp:98 x-google-profile-id=0\r\n"
+    "a=rtpmap:99 rtx/90000\r\n"
+    "a=fmtp:99 apt=98\r\n"
+    "a=rtpmap:100 red/90000\r\n"
+    "a=rtpmap:101 rtx/90000\r\n"
+    "a=fmtp:101 apt=100\r\n"
+    "a=rtpmap:102 ulpfec/90000\r\n"
+    "a=ssrc-group:FID 3263949794 2166305097\r\n"
+    "a=ssrc:3263949794 cname:rNEKgm1NFupmwR4x\r\n"
+    "a=ssrc:3263949794 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
+    "6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
+    "a=ssrc:3263949794 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
+    "a=ssrc:3263949794 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
+    "a=ssrc:2166305097 cname:rNEKgm1NFupmwR4x\r\n"
+    "a=ssrc:2166305097 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
+    "6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
+    "a=ssrc:2166305097 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
+    "a=ssrc:2166305097 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n";
+
+static const char* kOfferSdpPlanBMultipleAudioTracks =
+    "v=0\r\n"
+    "o=- 6228437149521864740 2 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "t=0 0\r\n"
+    "a=group:BUNDLE audio\r\n"
+    "a=msid-semantic: WMS 46f8615e-7599-49f3-9a45-3cf0faf58614 "
+    "e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
+    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
+    "126\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:Nzla\r\n"
+    "a=ice-pwd:PL1APGM2pr773UoUOsj8jzBI\r\n"
+    "a=ice-options:trickle\r\n"
+    "a=fingerprint:sha-256 "
+    "DF:8F:89:33:68:AB:55:26:4E:81:CF:95:8C:71:B7:89:45:E7:05:7A:5D:A8:CF:BF:"
+    "60:AA:C7:42:F2:85:23:1D\r\n"
+    "a=setup:actpass\r\n"
+    "a=mid:audio\r\n"
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+    "a=sendrecv\r\n"
+    "a=rtcp-mux\r\n"
+    "a=rtpmap:111 opus/48000/2\r\n"
+    "a=rtcp-fb:111 transport-cc\r\n"
+    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
+    "a=rtpmap:103 ISAC/16000\r\n"
+    "a=rtpmap:104 ISAC/32000\r\n"
+    "a=rtpmap:9 G722/8000\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:106 CN/32000\r\n"
+    "a=rtpmap:105 CN/16000\r\n"
+    "a=rtpmap:13 CN/8000\r\n"
+    "a=rtpmap:110 telephone-event/48000\r\n"
+    "a=rtpmap:112 telephone-event/32000\r\n"
+    "a=rtpmap:113 telephone-event/16000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=ssrc:2716812081 cname:0QgfsHYGSuZjeg5/\r\n"
+    "a=ssrc:2716812081 msid:e01b7c23-2b77-4e09-bee7-4b9140e49647 "
+    "d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
+    "a=ssrc:2716812081 mslabel:e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
+    "a=ssrc:2716812081 label:d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
+    "a=ssrc:4092260337 cname:0QgfsHYGSuZjeg5/\r\n"
+    "a=ssrc:4092260337 msid:46f8615e-7599-49f3-9a45-3cf0faf58614 "
+    "6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n"
+    "a=ssrc:4092260337 mslabel:46f8615e-7599-49f3-9a45-3cf0faf58614\r\n"
+    "a=ssrc:4092260337 label:6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n";
+
 class RTCPeerConnectionTest : public testing::Test {
  public:
-  RTCPeerConnection* CreatePC(V8TestingScope& scope) {
+  RTCPeerConnection* CreatePC(V8TestingScope& scope,
+                              const String& sdpSemantics = String()) {
     RTCConfiguration config;
+    config.setSdpSemantics(sdpSemantics);
     RTCIceServer ice_server;
     ice_server.setURL("stun:fake.stun.url");
     HeapVector<RTCIceServer> ice_servers;
@@ -236,4 +572,78 @@
   EXPECT_FALSE(pc->GetTrack(track_component));
 }
 
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningNotShownWhenPlanBSpecified) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope, "plan-b");
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  // It doesn't matter the SDP, never show a warning if sdpSemantics was
+  // specified at construction.
+  sdp.setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpPlanBMultipleAudioTracks);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningNotShownWhenUnifiedPlanSpecified) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope, "unified-plan");
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  // It doesn't matter the SDP, never show a warning if sdpSemantics was
+  // specified at construction.
+  sdp.setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpPlanBMultipleAudioTracks);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningNotShownWhenInvalidSdp) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope);
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  sdp.setSdp("invalid sdp");
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningNotShownForSingleTracks) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope);
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  // Neither Unified Plan or Plan B SDP should result in a warning if only a
+  // single track per m= section is used.
+  sdp.setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+  sdp.setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningShownForComplexPlanB) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope);
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  sdp.setSdp(kOfferSdpPlanBMultipleAudioTracks);
+  ASSERT_TRUE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
+TEST_F(RTCPeerConnectionTest, PlanBSdpWarningNotShownForComplexUnifiedPlan) {
+  V8TestingScope scope;
+  Persistent<RTCPeerConnection> pc = CreatePC(scope);
+  RTCSessionDescriptionInit sdp;
+  sdp.setType("offer");
+  sdp.setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
+  ASSERT_FALSE(pc->ShouldShowComplexPlanBSdpWarning(sdp));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/permissions/permissions.cc b/third_party/blink/renderer/modules/permissions/permissions.cc
index 402001ad..4b57a301 100644
--- a/third_party/blink/renderer/modules/permissions/permissions.cc
+++ b/third_party/blink/renderer/modules/permissions/permissions.cc
@@ -140,7 +140,8 @@
   if (name == "payment-handler")
     return CreatePermissionDescriptor(PermissionName::PAYMENT_HANDLER);
   if (name == "background-fetch") {
-    if (!RuntimeEnabledFeatures::BackgroundFetchEnabled()) {
+    if (!OriginTrials::BackgroundFetchEnabled(
+            ExecutionContext::From(script_state))) {
       exception_state.ThrowTypeError("Background Fetch is not enabled.");
       return nullptr;
     }
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
index 1a664a7c..8caa652 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
@@ -112,13 +112,15 @@
 
 void WaitUntilObserver::WillDispatchEvent() {
   event_dispatch_time_ = WTF::CurrentTimeTicks();
-  // When handling a notificationclick or paymentrequest event, we want to
-  // allow one window to be focused or opened. These calls are allowed between
-  // the call to willDispatchEvent() and the last call to
+  // When handling a notificationclick, paymentrequest, or backgroundfetchclick
+  // event, we want to allow one window to be focused or opened. These calls are
+  // allowed between the call to willDispatchEvent() and the last call to
   // DecrementPendingPromiseCount(). If waitUntil() isn't called, that means
   // between willDispatchEvent() and didDispatchEvent().
-  if (type_ == kNotificationClick || type_ == kPaymentRequest)
+  if (type_ == kNotificationClick || type_ == kPaymentRequest ||
+      type_ == kBackgroundFetchClick) {
     execution_context_->AllowWindowInteraction();
+  }
 
   DCHECK_EQ(EventDispatchState::kInitial, event_dispatch_state_);
   event_dispatch_state_ = EventDispatchState::kDispatching;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
index 9dc2b84..718ef75d 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
+#include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_param_descriptor.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_processor.h"
@@ -36,24 +37,23 @@
 
 AudioWorkletGlobalScope* AudioWorkletGlobalScope::Create(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    v8::Isolate* isolate,
     WorkerThread* thread) {
-  return new AudioWorkletGlobalScope(std::move(creation_params), isolate,
-                                     thread);
+  return new AudioWorkletGlobalScope(std::move(creation_params), thread);
 }
 
 AudioWorkletGlobalScope::AudioWorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    v8::Isolate* isolate,
     WorkerThread* thread)
-    : ThreadedWorkletGlobalScope(std::move(creation_params), isolate, thread) {}
+    : WorkletGlobalScope(std::move(creation_params),
+                         thread->GetWorkerReportingProxy(),
+                         thread) {}
 
 AudioWorkletGlobalScope::~AudioWorkletGlobalScope() = default;
 
 void AudioWorkletGlobalScope::Dispose() {
   DCHECK(IsContextThread());
   is_closing_ = true;
-  ThreadedWorkletGlobalScope::Dispose();
+  WorkletGlobalScope::Dispose();
 }
 
 void AudioWorkletGlobalScope::registerProcessor(
@@ -401,7 +401,7 @@
 void AudioWorkletGlobalScope::Trace(blink::Visitor* visitor) {
   visitor->Trace(processor_definition_map_);
   visitor->Trace(processor_instances_);
-  ThreadedWorkletGlobalScope::Trace(visitor);
+  WorkletGlobalScope::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
index d92c005..ce29470 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/workers/threaded_worklet_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_param_descriptor.h"
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
@@ -46,14 +46,12 @@
 
 // This is constructed and destroyed on a worker thread, and all methods also
 // must be called on the worker thread.
-class MODULES_EXPORT AudioWorkletGlobalScope final
-    : public ThreadedWorkletGlobalScope {
+class MODULES_EXPORT AudioWorkletGlobalScope final : public WorkletGlobalScope {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
   static AudioWorkletGlobalScope* Create(
       std::unique_ptr<GlobalScopeCreationParams>,
-      v8::Isolate*,
       WorkerThread*);
   ~AudioWorkletGlobalScope() override;
   bool IsAudioWorkletGlobalScope() const final { return true; }
@@ -106,7 +104,6 @@
 
  private:
   AudioWorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
-                          v8::Isolate*,
                           WorkerThread*);
 
   bool is_closing_ = false;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
index 6195e9cf..2778e171 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
@@ -68,8 +68,7 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio-worklet"),
                "AudioWorkletThread::createWorkerGlobalScope");
-  return AudioWorkletGlobalScope::Create(std::move(creation_params),
-                                         GetIsolate(), this);
+  return AudioWorkletGlobalScope::Create(std::move(creation_params), this);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 8c0b114..ba7d1183 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -665,8 +665,8 @@
     "fonts/mac/core_text_font_format_support.cc",
     "fonts/mac/core_text_font_format_support.h",
     "fonts/mac/font_cache_mac.mm",
-    "fonts/mac/font_family_matcher_mac.h",
-    "fonts/mac/font_family_matcher_mac.mm",
+    "fonts/mac/font_matcher_mac.h",
+    "fonts/mac/font_matcher_mac.mm",
     "fonts/mac/font_platform_data_mac.mm",
     "fonts/ng_text_fragment_paint_info.h",
     "fonts/opentype/font_format_check.cc",
@@ -1678,7 +1678,7 @@
     "fonts/font_test.cc",
     "fonts/font_test_utilities.cc",
     "fonts/generic_font_family_settings_test.cc",
-    "fonts/mac/font_family_matcher_mac_test.mm",
+    "fonts/mac/font_matcher_mac_test.mm",
     "fonts/opentype/font_settings_test.cc",
     "fonts/opentype/open_type_vertical_data_test.cc",
     "fonts/orientation_iterator_test.cc",
diff --git a/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc b/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
index 417aab0..ce7d6b7e 100644
--- a/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
@@ -44,9 +44,10 @@
     int64_t total_encoded_data_length,
     int64_t total_encoded_body_length,
     int64_t total_decoded_body_length) {
-  original_client->DidFinishLoading(finish_time, total_encoded_data_length,
-                                    total_encoded_body_length,
-                                    total_decoded_body_length, false);
+  original_client->DidFinishLoading(
+      finish_time, total_encoded_data_length, total_encoded_body_length,
+      total_decoded_body_length, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
index 7eba080..8b60504 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
@@ -37,9 +37,10 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
-#include "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
+#include "third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
@@ -248,23 +249,29 @@
     const FontDescription& font_description,
     const FontFaceCreationParams& creation_params,
     float font_size,
-    AlternateFontName) {
+    AlternateFontName alternate_name) {
   NSFontTraitMask traits = font_description.Style() ? NSFontItalicTrait : 0;
   float size = font_size;
 
-  NSFont* ns_font = MatchNSFontFamily(creation_params.Family(), traits,
-                                      font_description.Weight(), size);
-  if (!ns_font)
+  NSFont* matched_font = nullptr;
+  if (alternate_name == AlternateFontName::kLocalUniqueFace &&
+      RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled()) {
+    matched_font = MatchUniqueFont(creation_params.Family(), size);
+  } else {
+    matched_font = MatchNSFontFamily(creation_params.Family(), traits,
+                                     font_description.Weight(), size);
+  }
+  if (!matched_font)
     return nullptr;
 
   NSFontManager* font_manager = [NSFontManager sharedFontManager];
   NSFontTraitMask actual_traits = 0;
   if (font_description.Style())
-    actual_traits = [font_manager traitsOfFont:ns_font];
-  NSInteger actual_weight = [font_manager weightOfFont:ns_font];
+    actual_traits = [font_manager traitsOfFont:matched_font];
+  NSInteger actual_weight = [font_manager weightOfFont:matched_font];
 
   NSFont* platform_font =
-      UseHinting() ? [ns_font screenFont] : [ns_font printerFont];
+      UseHinting() ? [matched_font screenFont] : [matched_font printerFont];
   NSInteger app_kit_weight = ToAppKitFontWeight(font_description.Weight());
 
   // TODO(eae): Remove once skia supports bold emoji. See
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm b/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm
deleted file mode 100644
index 6ed8ac7..0000000
--- a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
-
-#include <AppKit/AppKit.h>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/font_family_names.h"
-#include "third_party/blink/renderer/platform/mac/version_util_mac.h"
-
-@interface NSString (YosemiteAdditions)
-- (BOOL)containsString:(NSString*)string;
-@end
-
-namespace blink {
-
-void TestSystemFontContainsString(FontSelectionValue desired_weight,
-                                  NSString* substring) {
-  NSFont* font =
-      MatchNSFontFamily(FontFamilyNames::system_ui, 0, desired_weight, 11);
-  EXPECT_TRUE([font.description containsString:substring]);
-}
-
-TEST(FontFamilyMatcherMacTest, YosemiteFontWeights) {
-  if (!IsOS10_10())
-    return;
-
-  TestSystemFontContainsString(FontSelectionValue(100), @"-UltraLight");
-  TestSystemFontContainsString(FontSelectionValue(200), @"-Thin");
-  TestSystemFontContainsString(FontSelectionValue(300), @"-Light");
-  TestSystemFontContainsString(FontSelectionValue(400), @"-Regular");
-  TestSystemFontContainsString(FontSelectionValue(500), @"-Medium");
-  TestSystemFontContainsString(FontSelectionValue(600), @"-Bold");
-  TestSystemFontContainsString(FontSelectionValue(700), @"-Bold");
-  TestSystemFontContainsString(FontSelectionValue(800), @"-Heavy");
-  TestSystemFontContainsString(FontSelectionValue(900), @"-Heavy");
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h
similarity index 87%
rename from third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h
rename to third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h
index 679bc3e0..23de6b6c 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h
+++ b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h
@@ -24,8 +24,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_MATCHER_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_MATCHER_MAC_H_
 
 #include <AppKit/NSFontManager.h>
 #include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
@@ -39,9 +39,12 @@
                                           FontSelectionValue desired_weight,
                                           float size);
 
+PLATFORM_EXPORT NSFont* MatchUniqueFont(const AtomicString& unique_font_name,
+                                        float size);
+
 // Converts ablink::FontSelectionValue to the nearest AppKit font weight if
 // possible, otherwise returns the default font weight.
 int ToAppKitFontWeight(FontSelectionValue);
-}
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm
similarity index 84%
rename from third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm
rename to third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm
index 0c0a983..c6bced3e 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm
@@ -27,14 +27,16 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
+#import "third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h"
 
 #import <AppKit/AppKit.h>
 #import <Foundation/Foundation.h>
 #import <math.h>
+#include "base/stl_util.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/mac/version_util_mac.h"
 #import "third_party/blink/renderer/platform/wtf/hash_set.h"
+#import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
 #import "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
 @interface NSFont (YosemiteAdditions)
@@ -60,7 +62,7 @@
 
   size_t select_weight = roundf(font_weight / 100) - 1;
   DCHECK_GE(select_weight, 0ul);
-  DCHECK_LE(select_weight, arraysize(ns_font_weights));
+  DCHECK_LE(select_weight, base::size(ns_font_weights));
   CGFloat* return_weight =
       reinterpret_cast<CGFloat*>(&ns_font_weights[select_weight]);
   return *return_weight;
@@ -74,9 +76,7 @@
 
 const NSFontTraitMask IMPORTANT_FONT_TRAITS =
     (NSCompressedFontMask | NSCondensedFontMask | NSExpandedFontMask |
-     NSItalicFontMask |
-     NSNarrowFontMask |
-     NSPosterFontMask |
+     NSItalicFontMask | NSNarrowFontMask | NSPosterFontMask |
      NSSmallCapsFontMask);
 
 static BOOL AcceptableChoice(NSFontTraitMask desired_traits,
@@ -127,6 +127,43 @@
   return candidate_weight_delta_magnitude < chosen_weight_delta_magnitude;
 }
 
+NSFont* MatchUniqueFont(const AtomicString& unique_font_name, float size) {
+  // Testing with a large list of fonts available on Mac OS shows that matching
+  // for kCTFontNameAttribute matches postscript name as well as full font name.
+  WTF::RetainPtr<CFMutableDictionaryRef> attributes(
+      CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL));
+  WTF::RetainPtr<NSString> desired_name(unique_font_name);
+  CFDictionarySetValue(attributes.Get(), kCTFontNameAttribute,
+                       desired_name.Get());
+  WTF::RetainPtr<CFNumberRef> font_size =
+      CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &size);
+  CFDictionarySetValue(attributes.Get(), kCTFontSizeAttribute, font_size.Get());
+  WTF::RetainPtr<CTFontDescriptorRef> descriptor(
+      CTFontDescriptorCreateWithAttributes(attributes.Get()));
+  WTF::RetainPtr<CTFontRef> matched_font(
+      CTFontCreateWithFontDescriptor(descriptor.Get(), 0, nullptr));
+  // CoreText will usually give us *something* but not always an exactly
+  // matched font.
+  DCHECK(matched_font.Get());
+  WTF::RetainPtr<CFStringRef> matched_font_ps_name(
+      CTFontCopyName(matched_font.Get(), kCTFontPostScriptNameKey));
+  WTF::RetainPtr<CFStringRef> matched_font_full_font_name(
+      CTFontCopyName(matched_font.Get(), kCTFontFullNameKey));
+  // If the found font does not match in postscript name or full font name, it's
+  // not the exact match that is required, so return nullptr.
+  if ((kCFCompareEqualTo !=
+       CFStringCompare(matched_font_ps_name.Get(),
+                       (__bridge CFStringRef)desired_name.Get(),
+                       kCFCompareCaseInsensitive)) &&
+      (kCFCompareEqualTo !=
+       CFStringCompare(matched_font_full_font_name.Get(),
+                       (__bridge CFStringRef)desired_name.Get(),
+                       kCFCompareCaseInsensitive))) {
+    return nullptr;
+  }
+  return (__bridge NSFont*)matched_font.LeakRef();
+}
+
 // Family name is somewhat of a misnomer here.  We first attempt to find an
 // exact match comparing the desiredFamily to the PostScript name of the
 // installed fonts.  If that fails we then do a search based on the family
@@ -308,7 +345,7 @@
       12,  // FontWeight900
   };
   DCHECK_GE(select_weight, 0ul);
-  DCHECK_LE(select_weight, arraysize(app_kit_font_weights));
+  DCHECK_LE(select_weight, base::size(app_kit_font_weights));
   return app_kit_font_weights[select_weight];
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac_test.mm b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac_test.mm
new file mode 100644
index 0000000..67476f5a
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac_test.mm
@@ -0,0 +1,93 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h"
+
+#include <AppKit/AppKit.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/mac/version_util_mac.h"
+
+@interface NSString (YosemiteAdditions)
+- (BOOL)containsString:(NSString*)string;
+@end
+
+namespace blink {
+
+void TestSystemFontContainsString(FontSelectionValue desired_weight,
+                                  NSString* substring) {
+  NSFont* font =
+      MatchNSFontFamily(FontFamilyNames::system_ui, 0, desired_weight, 11);
+  EXPECT_TRUE([font.description containsString:substring]);
+}
+
+TEST(FontMatcherMacTest, YosemiteFontWeights) {
+  if (!IsOS10_10())
+    return;
+
+  TestSystemFontContainsString(FontSelectionValue(100), @"-UltraLight");
+  TestSystemFontContainsString(FontSelectionValue(200), @"-Thin");
+  TestSystemFontContainsString(FontSelectionValue(300), @"-Light");
+  TestSystemFontContainsString(FontSelectionValue(400), @"-Regular");
+  TestSystemFontContainsString(FontSelectionValue(500), @"-Medium");
+  TestSystemFontContainsString(FontSelectionValue(600), @"-Bold");
+  TestSystemFontContainsString(FontSelectionValue(700), @"-Bold");
+  TestSystemFontContainsString(FontSelectionValue(800), @"-Heavy");
+  TestSystemFontContainsString(FontSelectionValue(900), @"-Heavy");
+}
+
+TEST(FontMatcherMacTest, NoUniqueFontMatchOnUnavailableFont) {
+  NSFont* font = MatchUniqueFont(
+      "ThisFontNameDoesNotExist07F444B9-4DDF-4A41-8F30-C80D4ED4CCA2", 12);
+  EXPECT_FALSE(font);
+}
+
+// If these font names are unavaiable on future Mac OS versions, please try to
+// find replacements or remove individual lines.
+TEST(FontMatcherMacTest, MatchFullFontName) {
+  const char* font_names[] = {"American Typewriter Condensed Light",
+                              "Arial Narrow Bold Italic",
+                              "Baskerville SemiBold Italic",
+                              "Devanagari MT",
+                              "DIN Alternate Bold",
+                              "Gill Sans Light Italic",
+                              "Iowan Old Style Titling",
+                              "Malayalam Sangam MN",
+                              "Hiragino Maru Gothic Pro W4",
+                              "Hiragino Kaku Gothic StdN W8"};
+
+  for (const char* font_name : font_names) {
+    @autoreleasepool {
+      NSFont* font = MatchUniqueFont(font_name, 12);
+      EXPECT_TRUE(font);
+    }
+  }
+}
+
+// If these font names are unavaiable on future Mac OS versions, please try to
+// find replacements or remove individual lines.
+TEST(FontMatcherMacTest, MatchPostscriptName) {
+  const char* font_names[] = {
+      "AmericanTypewriter-CondensedLight",
+      "ArialNarrow-BoldItalic",
+      "Baskerville-SemiBoldItalic",
+      "DevanagariMT",
+      "DINAlternate-Bold",
+      "GillSans-LightItalic",
+      "IowanOldStyle-Titling",
+      "MalayalamSangamMN",
+      "HiraMaruPro-W4",
+      "HiraKakuStdN-W8",
+  };
+
+  for (const char* font_name : font_names) {
+    @autoreleasepool {
+      NSFont* font = MatchUniqueFont(font_name, 12);
+      EXPECT_TRUE(font);
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
index 26ab2543..b4fa63f 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
@@ -1078,6 +1078,11 @@
   // Color components of opaque and transparent 16 bit PNG, read with libpng
   // in BigEndian and scaled to [0,1]. The values are read from non-interlaced
   // samples, but used for both interlaced and non-interlaced test cases.
+  // The sample pngs were all created by color converting the 8 bit sRGB source
+  // in Adobe Photoshop 18. The only exception is e-sRGB test case, for which
+  // Adobe software created a non-matching color profile (see crbug.com/874939).
+  // Hence, SkEncoder was used to generate the e-sRGB file (see the skia fiddle
+  // here: https://fiddle.skia.org/c/17beedfd66dac1ec930f0c414c50f847).
   static const std::vector<float> source_pixels_opaque_srgb = {
       0.4986953536, 0.5826657511, 0.7013199054, 1,   // Top left pixel
       0.907988098,  0.8309605554, 0.492011902,  1,   // Top right pixel
@@ -1094,10 +1099,10 @@
       0.772121767,  0.9671625849, 0.973510338,  1,   // Bottom left pixel
       0.9118944076, 0.9645685512, 0.9110704204, 1};  // Bottom right pixel
   static const std::vector<float> source_pixels_opaque_e_srgb = {
-      0.6414435035, 0.6857862211, 0.747005417,  1,   // Top left pixel
-      0.877347982,  0.8382848859, 0.6494087129, 1,   // Top right pixel
-      0.735194934,  0.9353933013, 0.9374380102, 1,   // Bottom left pixel
-      0.9209277485, 0.9575799191, 0.9264515145, 1};  // Bottom right pixel
+      0.6977539062, 0.5839843750, 0.4978027344, 1,   // Top left pixel
+      0.4899902344, 0.8310546875, 0.9096679688, 1,   // Top right pixel
+      0.9760742188, 0.9721679688, 0.6230468750, 1,   // Bottom left pixel
+      0.9057617188, 0.9643554688, 0.8940429688, 1};  // Bottom right pixel
   static const std::vector<float> source_pixels_opaque_prophoto = {
       0.5032883192, 0.5191271839, 0.6309147784, 1,   // Top left pixel
       0.8184176394, 0.8002899214, 0.5526970321, 1,   // Top right pixel
@@ -1125,10 +1130,10 @@
       0.4302738994, 0.9179064622, 0.933806363,  0.4,   // Bottom left pixel
       0.5595330739, 0.8228122377, 0.5554436561, 0.2};  // Bottom right pixel
   static const std::vector<float> source_pixels_transparent_e_srgb = {
-      0.5517814908, 0.6072327764, 0.6837415122, 0.8,   // Top left pixel
-      0.7955901427, 0.7304646372, 0.4156557565, 0.6,   // Top right pixel
-      0.3380178531, 0.8385290303, 0.8435950256, 0.4,   // Bottom left pixel
-      0.6046997787, 0.7879606317, 0.6323186084, 0.2};  // Bottom right pixel
+      0.6230468750, 0.4782714844, 0.3723144531, 0.8,   // Top left pixel
+      0.1528320312, 0.7172851562, 0.8466796875, 0.6,   // Top right pixel
+      0.9409179688, 0.9331054688, 0.0588073730, 0.4,   // Bottom left pixel
+      0.5253906250, 0.8310546875, 0.4743652344, 0.2};  // Bottom right pixel
   static const std::vector<float> source_pixels_transparent_prophoto = {
       0.379064622,  0.3988708324, 0.5386282139, 0.8,   // Top left pixel
       0.6973525597, 0.6671396963, 0.2544289311, 0.6,   // Top right pixel
@@ -1174,10 +1179,8 @@
 static std::vector<PNGSample> GetPNGSamplesInfo(bool include_8bit_pngs) {
   std::vector<PNGSample> png_samples;
   std::vector<String> interlace_status = {"", "_interlaced"};
-  // TODO(zakerinasab) https://crbug.com/874939:
-  // e-sRGB decodes fine to 8888, but fails to decode to F16, hence not tested.
-  std::vector<String> color_spaces = {"sRGB", "AdobeRGB", "DisplayP3",
-                                      "ProPhoto", "Rec2020"};
+  std::vector<String> color_spaces = {"sRGB",   "AdobeRGB", "DisplayP3",
+                                      "e-sRGB", "ProPhoto", "Rec2020"};
   std::vector<String> alpha_status = {"_opaque", "_transparent"};
 
   for (String color_space : color_spaces) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index c37d0c9..f2ef796 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1561,11 +1561,14 @@
   resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded);
 }
 
-void ResourceFetcher::HandleLoaderFinish(Resource* resource,
-                                         TimeTicks finish_time,
-                                         LoaderFinishType type,
-                                         uint32_t inflight_keepalive_bytes,
-                                         bool should_report_corb_blocking) {
+void ResourceFetcher::HandleLoaderFinish(
+    Resource* resource,
+    TimeTicks finish_time,
+    LoaderFinishType type,
+    uint32_t inflight_keepalive_bytes,
+    bool should_report_corb_blocking,
+    const std::vector<network::cors::PreflightTimingInfo>&
+        cors_preflight_timing_info) {
   DCHECK(resource);
 
   DCHECK_LE(inflight_keepalive_bytes, inflight_keepalive_bytes_);
@@ -1615,6 +1618,31 @@
         Context().AddResourceTiming(*info);
       resource->ReportResourceTimingToClients(*info);
     }
+
+    // Store additional timing info if CORS preflights are performed.
+    for (const auto& timing_info : cors_preflight_timing_info) {
+      // InitiatorType and InitialURL should be the same with each of the
+      // original request.
+      scoped_refptr<ResourceTimingInfo> preflight_info =
+          ResourceTimingInfo::Create(info->InitiatorType(),
+                                     timing_info.start_time, false);
+      preflight_info->SetInitialURL(info->InitialURL());
+      preflight_info->SetLoadFinishTime(timing_info.finish_time);
+      preflight_info->AddFinalTransferSize(timing_info.transfer_size);
+
+      // Set a provisional response to provide possible other information.
+      ResourceResponse response(info->InitialURL());
+      response.SetAlpnNegotiatedProtocol(
+          WebString::FromUTF8(timing_info.alpn_negotiated_protocol));
+      response.SetConnectionInfo(timing_info.connection_info);
+      response.SetHTTPHeaderField(
+          HTTPNames::Timing_Allow_Origin,
+          WebString::FromUTF8(timing_info.timing_allow_origin));
+      response.SetEncodedDataLength(timing_info.transfer_size);
+      preflight_info->SetFinalResponse(response);
+
+      Context().AddResourceTiming(*preflight_info);
+    }
   }
 
   resource->VirtualTimePauser().UnpauseVirtualTime();
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index bf5a162e..1bb482a3 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -28,6 +28,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
 
 #include <memory>
+
+#include "services/network/public/cpp/cors/preflight_timing_info.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
@@ -141,7 +143,9 @@
                           TimeTicks finish_time,
                           LoaderFinishType,
                           uint32_t inflight_keepalive_bytes,
-                          bool should_report_corb_blocking);
+                          bool should_report_corb_blocking,
+                          const std::vector<network::cors::PreflightTimingInfo>&
+                              cors_preflight_timing_info);
   void HandleLoaderError(Resource*,
                          const ResourceError&,
                          uint32_t inflight_keepalive_bytes);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 36b8959..5ec4206 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -237,8 +237,9 @@
       fetcher->GetNavigationTimingInfo();
   ASSERT_TRUE(navigation_timing_info);
   long long encoded_data_length = 123;
-  resource->Loader()->DidFinishLoading(TimeTicks(), encoded_data_length, 0, 0,
-                                       false);
+  resource->Loader()->DidFinishLoading(
+      TimeTicks(), encoded_data_length, 0, 0, false,
+      std::vector<network::cors::PreflightTimingInfo>());
   EXPECT_EQ(navigation_timing_info->TransferSize(), encoded_data_length);
 
   // When there are redirects.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index 7c26a50..7e7ee3d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -887,16 +887,20 @@
       resource_->Identifier(),
       network_instrumentation::RequestOutcome::kSuccess);
 
-  fetcher_->HandleLoaderFinish(resource_.Get(), TimeTicks(),
-                               ResourceFetcher::kDidFinishFirstPartInMultipart,
-                               0, false);
+  fetcher_->HandleLoaderFinish(
+      resource_.Get(), TimeTicks(),
+      ResourceFetcher::kDidFinishFirstPartInMultipart, 0, false,
+      std::vector<network::cors::PreflightTimingInfo>());
 }
 
-void ResourceLoader::DidFinishLoading(TimeTicks finish_time,
-                                      int64_t encoded_data_length,
-                                      int64_t encoded_body_length,
-                                      int64_t decoded_body_length,
-                                      bool should_report_corb_blocking) {
+void ResourceLoader::DidFinishLoading(
+    TimeTicks finish_time,
+    int64_t encoded_data_length,
+    int64_t encoded_body_length,
+    int64_t decoded_body_length,
+    bool should_report_corb_blocking,
+    const std::vector<network::cors::PreflightTimingInfo>&
+        cors_preflight_timing_info) {
   resource_->SetEncodedDataLength(encoded_data_length);
   resource_->SetEncodedBodyLength(encoded_body_length);
   resource_->SetDecodedBodyLength(decoded_body_length);
@@ -919,7 +923,8 @@
 
   fetcher_->HandleLoaderFinish(
       resource_.Get(), finish_time, ResourceFetcher::kDidFinishLoading,
-      inflight_keepalive_bytes_, should_report_corb_blocking);
+      inflight_keepalive_bytes_, should_report_corb_blocking,
+      cors_preflight_timing_info);
 }
 
 void ResourceLoader::DidFail(const WebURLError& error,
@@ -1014,7 +1019,8 @@
     FinishedCreatingBlob(blob);
   }
   DidFinishLoading(CurrentTimeTicks(), encoded_data_length, encoded_body_length,
-                   decoded_body_length, false);
+                   decoded_body_length, false,
+                   std::vector<network::cors::PreflightTimingInfo>());
 }
 
 void ResourceLoader::Dispose() {
@@ -1097,7 +1103,8 @@
     DidFinishLoading(load_did_finish_before_blob_->finish_time,
                      response.EncodedDataLength(), response.EncodedBodyLength(),
                      response.DecodedBodyLength(),
-                     load_did_finish_before_blob_->should_report_corb_blocking);
+                     load_did_finish_before_blob_->should_report_corb_blocking,
+                     std::vector<network::cors::PreflightTimingInfo>());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
index 6011ec2d..ed7a4a5 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -120,11 +120,13 @@
   void DidReceiveTransferSizeUpdate(int transfer_size_diff) override;
   void DidStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
-  void DidFinishLoading(TimeTicks finish_time,
-                        int64_t encoded_data_length,
-                        int64_t encoded_body_length,
-                        int64_t decoded_body_length,
-                        bool should_report_corb_blocking) override;
+  void DidFinishLoading(
+      TimeTicks finish_time,
+      int64_t encoded_data_length,
+      int64_t encoded_body_length,
+      int64_t decoded_body_length,
+      bool should_report_corb_blocking,
+      const std::vector<network::cors::PreflightTimingInfo>&) override;
   void DidFail(const WebURLError&,
                int64_t encoded_data_length,
                int64_t encoded_body_length,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 3fceef2..7a31007 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -114,6 +114,7 @@
     },
     {
       name: "BackgroundFetch",
+      origin_trial_feature_name: "BackgroundFetch",
       status: "experimental",
     },
     {
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.cc b/third_party/blink/renderer/platform/text/text_break_iterator.cc
index d9cd41f..e39e8df0 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.cc
@@ -298,8 +298,8 @@
           BreakSpaceType break_space>
 inline int LazyLineBreakIterator::NextBreakablePosition(
     int pos,
-    const CharacterType* str) const {
-  int len = static_cast<int>(string_.length());
+    const CharacterType* str,
+    int len) const {
   DCHECK_GE(pos, 0);
   DCHECK_GE(static_cast<unsigned>(pos), start_offset_);
   int next_break = -1;
@@ -379,30 +379,35 @@
 template <typename CharacterType, LineBreakType lineBreakType>
 inline int LazyLineBreakIterator::NextBreakablePosition(
     int pos,
-    const CharacterType* str) const {
+    const CharacterType* str,
+    int len) const {
   switch (break_space_) {
     case BreakSpaceType::kBeforeEverySpace:
       return NextBreakablePosition<CharacterType, lineBreakType,
-                                   BreakSpaceType::kBeforeEverySpace>(pos, str);
+                                   BreakSpaceType::kBeforeEverySpace>(pos, str,
+                                                                      len);
     case BreakSpaceType::kBeforeSpaceRun:
       return NextBreakablePosition<CharacterType, lineBreakType,
-                                   BreakSpaceType::kBeforeSpaceRun>(pos, str);
+                                   BreakSpaceType::kBeforeSpaceRun>(pos, str,
+                                                                    len);
   }
   NOTREACHED();
   return NextBreakablePosition<CharacterType, lineBreakType,
-                               BreakSpaceType::kBeforeEverySpace>(pos, str);
+                               BreakSpaceType::kBeforeEverySpace>(pos, str,
+                                                                  len);
 }
 
 template <LineBreakType lineBreakType>
-inline int LazyLineBreakIterator::NextBreakablePosition(int pos) const {
+inline int LazyLineBreakIterator::NextBreakablePosition(int pos,
+                                                        int len) const {
   if (UNLIKELY(string_.IsNull()))
     return 0;
   if (string_.Is8Bit()) {
-    return NextBreakablePosition<LChar, lineBreakType>(pos,
-                                                       string_.Characters8());
+    return NextBreakablePosition<LChar, lineBreakType>(
+        pos, string_.Characters8(), len);
   }
-  return NextBreakablePosition<UChar, lineBreakType>(pos,
-                                                     string_.Characters16());
+  return NextBreakablePosition<UChar, lineBreakType>(
+      pos, string_.Characters16(), len);
 }
 
 int LazyLineBreakIterator::NextBreakablePositionBreakCharacter(int pos) const {
@@ -415,16 +420,16 @@
   return next != kTextBreakDone ? next + start_offset_ : string_.length();
 }
 
-int LazyLineBreakIterator::NextBreakablePosition(
-    int pos,
-    LineBreakType line_break_type) const {
+int LazyLineBreakIterator::NextBreakablePosition(int pos,
+                                                 LineBreakType line_break_type,
+                                                 int len) const {
   switch (line_break_type) {
     case LineBreakType::kNormal:
-      return NextBreakablePosition<LineBreakType::kNormal>(pos);
+      return NextBreakablePosition<LineBreakType::kNormal>(pos, len);
     case LineBreakType::kBreakAll:
-      return NextBreakablePosition<LineBreakType::kBreakAll>(pos);
+      return NextBreakablePosition<LineBreakType::kBreakAll>(pos, len);
     case LineBreakType::kKeepAll:
-      return NextBreakablePosition<LineBreakType::kKeepAll>(pos);
+      return NextBreakablePosition<LineBreakType::kKeepAll>(pos, len);
     case LineBreakType::kBreakCharacter:
       return NextBreakablePositionBreakCharacter(pos);
   }
@@ -432,9 +437,15 @@
   return NextBreakablePosition(pos, LineBreakType::kNormal);
 }
 
+int LazyLineBreakIterator::NextBreakablePosition(
+    int pos,
+    LineBreakType line_break_type) const {
+  return NextBreakablePosition(pos, line_break_type,
+                               static_cast<int>(string_.length()));
+}
+
 unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset) const {
-  int next_break = -1;
-  IsBreakable(offset, next_break);
+  int next_break = NextBreakablePosition(offset, break_type_);
   DCHECK_GE(next_break, 0);
   return next_break;
 }
@@ -442,10 +453,7 @@
 unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset,
                                                      unsigned len) const {
   DCHECK_LE(len, string_.length());
-  // TODO(kojii): |len| is not utilized for perf benefit as
-  // |NextBreakablePosition| does not accept it due to a revert in
-  // crbug.com/883963.
-  int next_break = NextBreakablePosition(offset, break_type_);
+  int next_break = NextBreakablePosition(offset, break_type_, len);
   DCHECK_GE(next_break, 0);
   return next_break;
 }
@@ -453,9 +461,20 @@
 unsigned LazyLineBreakIterator::PreviousBreakOpportunity(unsigned offset,
                                                          unsigned min) const {
   unsigned pos = std::min(offset, string_.length());
-  for (; pos > min; pos--) {
-    if (IsBreakable(pos))
-      return pos;
+  // +2 to ensure at least one code point is included.
+  unsigned end = std::min(pos + 2, string_.length());
+  while (pos > min) {
+    int next_break = NextBreakablePosition(pos, break_type_, end);
+    DCHECK_GE(next_break, 0);
+    if (static_cast<unsigned>(next_break) == pos)
+      return next_break;
+
+    // There's no break opportunities at |pos| or after.
+    end = pos;
+    if (string_.Is8Bit())
+      --pos;
+    else
+      U16_BACK_1(string_.Characters16(), 0, pos);
   }
   return min;
 }
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.h b/third_party/blink/renderer/platform/text/text_break_iterator.h
index e2ce60c0..f7f02eb 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.h
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -242,7 +242,8 @@
     // Limit length to pos + 1.
     // TODO(layout-dev): We should probably try to break out an actual
     // IsBreakable method from NextBreakablePosition and get rid of this hack.
-    int next_breakable = NextBreakablePosition(pos, break_type_);
+    int len = std::min(pos + 1, static_cast<int>(string_.length()));
+    int next_breakable = NextBreakablePosition(pos, break_type_, len);
     return pos == next_breakable;
   }
 
@@ -305,12 +306,13 @@
   }
 
   template <typename CharacterType, LineBreakType, BreakSpaceType>
-  int NextBreakablePosition(int pos, const CharacterType* str) const;
+  int NextBreakablePosition(int pos, const CharacterType* str, int len) const;
   template <typename CharacterType, LineBreakType>
-  int NextBreakablePosition(int pos, const CharacterType* str) const;
+  int NextBreakablePosition(int pos, const CharacterType* str, int len) const;
   template <LineBreakType>
-  int NextBreakablePosition(int pos) const;
+  int NextBreakablePosition(int pos, int len) const;
   int NextBreakablePositionBreakCharacter(int pos) const;
+  int NextBreakablePosition(int pos, LineBreakType, int len) const;
   int NextBreakablePosition(int pos, LineBreakType) const;
 
   static const unsigned kPriorContextCapacity = 2;
diff --git a/third_party/blink/tools/blinkpy/common/net/buildbot.py b/third_party/blink/tools/blinkpy/common/net/buildbot.py
index cb8159bb..b218456 100644
--- a/third_party/blink/tools/blinkpy/common/net/buildbot.py
+++ b/third_party/blink/tools/blinkpy/common/net/buildbot.py
@@ -72,6 +72,8 @@
         if build_number:
             assert str(build_number).isdigit(), 'expected numeric build number, got %s' % build_number
             url_base = self.builder_results_url_base(builder_name)
+            if step_name is None:
+                step_name = self.get_layout_test_step_name(Build(builder_name, build_number))
             if step_name:
                 return '%s/%s/%s/layout-test-results' % (
                     url_base, build_number, urllib.quote(step_name))
@@ -120,7 +122,7 @@
             'buildnumber': build.build_number,
             'name': 'full_results.json',
             # This forces the server to gives us JSON rather than an HTML page.
-            'callback': 'ADD_RESULTS',
+            'callback': json_results_generator.JSON_CALLBACK,
         }))
         data = NetworkTransaction(return_none_on_404=True).run(
             lambda: self.fetch_file(url))
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
index 3d18489..8e3a820f 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -76,6 +76,10 @@
     build_number_option = optparse.make_option(
         '--build-number', default=None, type='int',
         help='Optional build number; if not given, the latest build is used.')
+    step_name_option = optparse.make_option(
+        '--step-name',
+        help=('Name of the step which ran the actual tests, and which '
+              'should be used to retrieve results from.'))
 
     def __init__(self, options=None):
         super(AbstractRebaseliningCommand, self).__init__(options=options)
@@ -296,7 +300,6 @@
                 '--port-name', port_name,
             ])
 
-
             copy_command = [self._tool.executable, path_to_blink_tool, 'copy-existing-baselines-internal'] + args
             copy_baseline_commands.append(tuple([copy_command, cwd]))
 
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
index 6b7c098..a920cd9e 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
@@ -412,8 +412,8 @@
         self.assertLog([
             'INFO: Finished try jobs found for all try bots.\n',
             'INFO: Failed to fetch results for "MOCK Try Win".\n',
-            ('INFO: Results URL: https://test-results.appspot.com/data/layout_results'
-             '/MOCK_Try_Win/5000/layout-test-results/results.html\n'),
+            ('INFO: Results URL: https://test-results.appspot.com/data/layout_results/'
+             'MOCK_Try_Win/5000/webkit_layout_tests%20%28with%20patch%29/layout-test-results/results.html\n'),
             'INFO: There are some builders with no results:\n',
             'INFO:   MOCK Try Win\n',
             'INFO: Would you like to continue?\n',
@@ -429,8 +429,8 @@
         self.assertLog([
             'INFO: Finished try jobs found for all try bots.\n',
             'INFO: Failed to fetch results for "MOCK Try Win".\n',
-            ('INFO: Results URL: https://test-results.appspot.com/data/layout_results'
-             '/MOCK_Try_Win/5000/layout-test-results/results.html\n'),
+            ('INFO: Results URL: https://test-results.appspot.com/data/layout_results/'
+             'MOCK_Try_Win/5000/webkit_layout_tests%20%28with%20patch%29/layout-test-results/results.html\n'),
             'INFO: There are some builders with no results:\n',
             'INFO:   MOCK Try Win\n',
             'INFO: For one/flaky-fail.html:\n',
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_test.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_test.py
index 665c81d..7640cd7 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_test.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_test.py
@@ -4,7 +4,6 @@
 
 import json
 import logging
-import optparse
 
 from blinkpy.tool.commands.rebaseline import AbstractRebaseliningCommand
 
@@ -22,11 +21,8 @@
             self.port_name_option,
             self.builder_option,
             self.build_number_option,
+            self.step_name_option,
             self.results_directory_option,
-            optparse.make_option(
-                '--step-name',
-                help=('Name of the step which ran the actual tests, and which '
-                      'should be used to retrieve results from.'))
         ])
 
     def execute(self, options, args, tool):
diff --git a/third_party/blink/tools/blinkpy/web_tests/layout_package/json_results_generator.py b/third_party/blink/tools/blinkpy/web_tests/layout_package/json_results_generator.py
index c09ad21..2549b49 100644
--- a/third_party/blink/tools/blinkpy/web_tests/layout_package/json_results_generator.py
+++ b/third_party/blink/tools/blinkpy/web_tests/layout_package/json_results_generator.py
@@ -31,7 +31,8 @@
 
 _log = logging.getLogger(__name__)
 
-_JSON_PREFIX = "ADD_RESULTS("
+JSON_CALLBACK = "ADD_RESULTS"
+_JSON_PREFIX = JSON_CALLBACK + "("
 _JSON_SUFFIX = ");"
 
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
index 791c8adb..2e81b05 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
@@ -318,6 +318,8 @@
                       "'unexpected' == Ignore any tests that had unexpected results on the bot.")),
             optparse.make_option(
                 '--iterations',
+                '--isolated-script-test-repeat',
+                # TODO(crbug.com/893235): Remove the gtest alias when FindIt no longer uses it.
                 '--gtest_repeat',
                 type='int',
                 default=1,
@@ -361,6 +363,7 @@
             optparse.make_option(
                 '--num-retries',
                 '--test-launcher-retry-limit',
+                '--isolated-script-test-launcher-retry-limit',
                 type='int',
                 default=None,
                 help=('Number of times to retry failures. Default (when this '
@@ -401,11 +404,13 @@
                       '"only" == only run the SKIP tests, '
                       '"always" == always skip, even if listed on the command line.')),
             optparse.make_option(
+                '--isolated-script-test-also-run-disabled-tests',
+                # TODO(crbug.com/893235): Remove the gtest alias when FindIt no longer uses it.
                 '--gtest_also_run_disabled_tests',
-                action='store_true',
-                default=False,  # Consistent with the default value of --skipped
-                help=('Equivalent to --skipped=ignore. This option overrides '
-                      '--skipped if both are given.')),
+                action='store_const',
+                const='ignore',
+                dest='skipped',
+                help=('Equivalent to --skipped=ignore.')),
             optparse.make_option(
                 '--skip-failing-tests',
                 action='store_true',
@@ -429,6 +434,12 @@
                 metavar='FILE',
                 help='read list of tests to run from file'),
             optparse.make_option(
+                '--isolated-script-test-filter',
+                type='string',
+                help='A list of tests to run separated by TWO colons, e.g. fast::css/test.html, '
+                     'same as listing them as positional arguments'),
+            # TODO(crbug.com/893235): Remove gtest_filter when FindIt no longer uses it.
+            optparse.make_option(
                 '--gtest_filter',
                 type='string',
                 help='A colon-separated list of tests to run. Wildcards are '
@@ -553,11 +564,12 @@
         if not options.skipped:
             options.skipped = 'always'
 
-    if options.gtest_also_run_disabled_tests:
-        options.skipped = 'ignore'
-    elif not options.skipped:
+    if not options.skipped:
         options.skipped = 'default'
 
+    if options.isolated_script_test_filter:
+        args.extend(options.isolated_script_test_filter.split('::'))
+
     if options.gtest_filter:
         args.extend(options.gtest_filter.split(':'))
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
index e248e27d..153ffa83 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
@@ -443,15 +443,15 @@
         # Now check that we don't run anything.
         self.assertEqual(get_tests_run(['--skipped=always', 'passes/skipped/skip.html']), [])
 
-    def test_gtest_also_run_disabled_tests(self):
+    def test_isolated_script_test_also_run_disabled_tests(self):
         self.assertEqual(
-            sorted(get_tests_run(['--gtest_also_run_disabled_tests', 'passes'])),
+            sorted(get_tests_run(['--isolated-script-test-also-run-disabled-tests', 'passes'])),
             sorted(get_tests_run(['--skipped=ignore', 'passes']))
         )
 
-    def test_gtest_also_run_disabled_tests_overrides_skipped(self):
+    def test_gtest_also_run_disabled_tests(self):
         self.assertEqual(
-            sorted(get_tests_run(['--gtest_also_run_disabled_tests', '--skipped=always', 'passes'])),
+            sorted(get_tests_run(['--gtest_also_run_disabled_tests', 'passes'])),
             sorted(get_tests_run(['--skipped=ignore', 'passes']))
         )
 
@@ -522,6 +522,14 @@
         tests_run = get_tests_run(['--test-list=%s' % filename], host=host)
         self.assertEqual(['passes/text.html'], tests_run)
 
+    def test_isolated_script_test_filter(self):
+        host = MockHost()
+        tests_run = get_tests_run(
+            ['--isolated-script-test-filter=passes/text.html::passes/image.html', 'passes/error.html'],
+            host=host
+        )
+        self.assertEqual(sorted(tests_run), ['passes/error.html', 'passes/image.html', 'passes/text.html'])
+
     def test_gtest_filter(self):
         host = MockHost()
         tests_run = get_tests_run(['--gtest_filter=passes/text.html:passes/image.html', 'passes/error.html'], host=host)
diff --git a/third_party/blink/tools/blinkpy/web_tests/try_flag_unittest.py b/third_party/blink/tools/blinkpy/web_tests/try_flag_unittest.py
index 27c1f85..2283d8d 100644
--- a/third_party/blink/tools/blinkpy/web_tests/try_flag_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/try_flag_unittest.py
@@ -134,10 +134,11 @@
         TryFlag(cmd, host, MockGitCL(host, self.mock_try_results)).run()
 
         def results_url(build):
-            return '%s/%s/%s/layout-test-results/results.html' % (
+            return '%s/%s/%s/%s/layout-test-results/results.html' % (
                 'https://test-results.appspot.com/data/layout_results',
                 build.builder_name,
-                build.build_number
+                build.build_number,
+                'webkit_layout_tests%20%28with%20patch%29'
             )
         self.assertEqual(host.stdout.getvalue(), '\n'.join([
             'Fetching results...',
diff --git a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
index 61a5a80..17e1a56 100644
--- a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -1230,4 +1230,5 @@
 .align	2
 .align	2
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
index 08c8ea2c..9076eeb 100644
--- a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -1208,5 +1208,6 @@
 #endif
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
index 0343672..d4fd317 100644
--- a/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -1080,5 +1080,6 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
index 5cd02b7..3b6cf6a 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -1231,5 +1231,6 @@
 .align	2
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
index aefcc88..8bb535c 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -1209,6 +1209,7 @@
 #endif
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
index 18f909a..ac9d5f0 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -1081,6 +1081,7 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 9c28d79..532fa1b 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -5728,6 +5728,11 @@
   <description>User saw the UwS (harmful) interstitial.</description>
 </action>
 
+<action name="HatsBubble.Show">
+  <owner>jeffreycohen@chromium.org</owner>
+  <description>User saw a Happiness Tracking Survey.</description>
+</action>
+
 <action name="HiliteColor">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ff1f339..235b634 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9536,6 +9536,13 @@
   <int value="3" label="Connected to service with KeepAlive connection"/>
 </enum>
 
+<enum name="CustomTabsDynamicModuleDestructionReason">
+  <int value="0" label="No caching enabled and the module is unused"/>
+  <int value="1" label="Cached and severe memory pressure"/>
+  <int value="2" label="Cached and mild memory pressure and time exceeded"/>
+  <int value="3" label="Cached and app hidden and time exceeded"/>
+</enum>
+
 <enum name="CustomTabsDynamicModuleLoadResult">
   <int value="0" label="Loaded new instance successfully"/>
   <int value="1" label="Used cached instance"/>
@@ -14665,6 +14672,7 @@
   <int value="486" label="WebUsbAllowDevicesForUrls"/>
   <int value="487" label="BrowserSignin"/>
   <int value="488" label="SmartLockSigninAllowed"/>
+  <int value="489" label="NTLMShareAuthenticationEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -20399,6 +20407,8 @@
   <int value="2588" label="GetComputedStyleForWebkitAppearance"/>
   <int value="2589" label="CursorImageLE32x32"/>
   <int value="2590" label="CursorImageGT32x32"/>
+  <int value="2591"
+      label="RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -29022,6 +29032,8 @@
   <int value="-1426150007" label="ignore-previews-blacklist"/>
   <int value="-1426034869" label="NoCreditCardAbort:enabled"/>
   <int value="-1423348289" label="NupPrinting:disabled"/>
+  <int value="-1423193305"
+      label="AutofillRationalizeRepeatedServerPredictions:enabled"/>
   <int value="-1420542268" label="AutofillEnableAccountWalletStorage:disabled"/>
   <int value="-1419788257" label="enable-experimental-hotwording"/>
   <int value="-1417122729"
@@ -29057,6 +29069,7 @@
       label="ServiceWorkerImportedScriptUpdateCheck:disabled"/>
   <int value="-1365503870" label="enable-simplified-fullscreen-ui"/>
   <int value="-1363709707" label="MaterialDesignHistory:disabled"/>
+  <int value="-1361493814" label="FCMInvalidations:disabled"/>
   <int value="-1358669137" label="enable-supervised-user-blacklist"/>
   <int value="-1357778876" label="ExplicitLanguageAsk:enabled"/>
   <int value="-1357655121" label="enable-iframe-based-signin"/>
@@ -29291,6 +29304,8 @@
   <int value="-933377608"
       label="OmniboxUIExperimentHideSteadyStateUrlScheme:enabled"/>
   <int value="-933316841" label="enable-permissions-blacklist"/>
+  <int value="-929944930"
+      label="AutofillRationalizeRepeatedServerPredictions:disabled"/>
   <int value="-928138978" label="IPH_DemoMode:disabled"/>
   <int value="-926422468" label="disable-embedded-shared-worker"/>
   <int value="-920204598" label="ScrollAnchorSerialization:enabled"/>
@@ -29882,6 +29897,7 @@
   <int value="292560715" label="ViewsCastDialog:disabled"/>
   <int value="293134455" label="AutofillSendBillingCustomerNumber:disabled"/>
   <int value="293996306" label="ArrayPrototypeValues:disabled"/>
+  <int value="300095239" label="FCMInvalidations:enabled"/>
   <int value="301869874" label="NTPPhysicalWebPageSuggestions:disabled"/>
   <int value="303058039" label="AccountConsistency:disabled"/>
   <int value="303252119" label="AutofillExpandedPopupViews:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b4e3efc..9a6638ce 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -654,8 +654,25 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.ResourceUsage.Size.Cache" units="KB"
+    expires_after="2019-10-02">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    For a given resource fetched from the disk cache, logs the encoded body
+    length of the resource, even if the resource request was canceled or
+    incomplete. Recorded when the resource request is complete, or when the page
+    is destroyed/navigated for incomplete resources.
+  </summary>
+</histogram>
+
 <histogram name="Ads.ResourceUsage.Size.Mainframe.AdResource" units="KB"
     expires_after="2019-09-05">
+  <obsolete>
+    Deprecated 10/2018. Replaced with
+    Ads.ResourceUsage.Size.Network.Mainframe.AdResource and
+    Ads.ResourceUsage.Size.Cache.Mainframe.AdResource.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -666,6 +683,11 @@
 
 <histogram name="Ads.ResourceUsage.Size.Mainframe.VanillaResource" units="KB"
     expires_after="2019-09-05">
+  <obsolete>
+    Deprecated 10/2018. Replaced with
+    Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource and
+    Ads.ResourceUsage.Size.Cache.Mainframe.VanillaResource.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -677,6 +699,10 @@
 
 <histogram base="true" name="Ads.ResourceUsage.Size.Mime" units="KB"
     expires_after="2019-09-10">
+  <obsolete>
+    Deprecated 10/2018. Replaced with Ads.ResourceUsage.Size.Cache.Mime and
+    Ads.ResourceUsage.Size.Network.Mime.
+  </obsolete>
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -687,8 +713,25 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.ResourceUsage.Size.Network" units="KB"
+    expires_after="2019-10-02">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    For a given resource fetched from the network, logs the network bytes used
+    to load the resource (including headers), even if the resource request was
+    canceled or incomplete. Recorded when the resource request is complete, or
+    when the page is destroyed/navigated for incomplete resources.
+  </summary>
+</histogram>
+
 <histogram name="Ads.ResourceUsage.Size.Subframe.AdResource" units="KB"
     expires_after="2019-09-05">
+  <obsolete>
+    Deprecated 10/2018. Replaced with
+    Ads.ResourceUsage.Size.Network.Subframe.AdResource and
+    Ads.ResourceUsage.Size.Cache.Subframe.AdResource.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -699,6 +742,11 @@
 
 <histogram name="Ads.ResourceUsage.Size.Subframe.VanillaResource" units="KB"
     expires_after="2019-09-05">
+  <obsolete>
+    Deprecated 10/2018. Replaced with
+    Ads.ResourceUsage.Size.Network.Subframe.VanillaResource and
+    Ads.ResourceUsage.Size.Cache.Subframe.VanillaResource.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
@@ -16658,6 +16706,14 @@
   </summary>
 </histogram>
 
+<histogram name="CustomTabs.DynamicModule.DestructionReason"
+    enum="CustomTabsDynamicModuleDestructionReason" expires_after="2019-07-01">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Possible reasons for destroying a custom tabs dynamic module. Android only.
+  </summary>
+</histogram>
+
 <histogram name="CustomTabs.DynamicModule.EntryPointLoadClassTime" units="ms">
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -74077,6 +74133,56 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.StorePerformance.AddLogin"
+    expires_after="2019-03-01">
+  <owner>cfroussios@chromium.org</owner>
+  <owner>dvadym@chromium.org</owner>
+  <summary>
+    The time (ms) it takes for the synchronous part of the AddLogin on the
+    PasswordStore to complete.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.StorePerformance.GetLogin"
+    expires_after="2019-03-01">
+  <owner>cfroussios@chromium.org</owner>
+  <owner>dvadym@chromium.org</owner>
+  <summary>
+    The time (ms) it takes for the synchronous part of the GetLogin on the
+    PasswordStore to complete.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.StorePerformance.GetLogins"
+    expires_after="2019-03-01">
+  <owner>cfroussios@chromium.org</owner>
+  <owner>dvadym@chromium.org</owner>
+  <summary>
+    The time (ms) it takes for the synchronous part of the GetLogins on the
+    PasswordStore to complete.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.StorePerformance.RemoveLogin"
+    expires_after="2019-03-01">
+  <owner>cfroussios@chromium.org</owner>
+  <owner>dvadym@chromium.org</owner>
+  <summary>
+    The time (ms) it takes for the synchronous part of the RemoveLogin on the
+    PasswordStore to complete.
+  </summary>
+</histogram>
+
+<histogram name="PasswordManager.StorePerformance.UpdateLogin"
+    expires_after="2019-03-01">
+  <owner>cfroussios@chromium.org</owner>
+  <owner>dvadym@chromium.org</owner>
+  <summary>
+    The time (ms) it takes for the synchronous part of the UpdateLogin on the
+    PasswordStore to complete.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.StoreReadyWhenWiping" enum="Boolean">
   <obsolete>
     Deprecated since August 28, 2018, due to removing the corresponding feature.
@@ -81633,6 +81739,50 @@
   </summary>
 </histogram>
 
+<histogram name="Previews.OptimizationGuide.HintCache.HasHint.AtCommit"
+    enum="NQEEffectiveConnectionType">
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the effective connection type when the optimization guide hint cache
+    has a hint entry for a URL's host at commit time.
+  </summary>
+</histogram>
+
+<histogram name="Previews.OptimizationGuide.HintCache.HasHint.BeforeCommit"
+    enum="NQEEffectiveConnectionType">
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the effective connection type when the optimization guide hint cache
+    has a hint entry for a URL's host before commit time (e.g., at original
+    navigation time or redirected navigation time).
+  </summary>
+</histogram>
+
+<histogram name="Previews.OptimizationGuide.HintCache.HostMatch.AtCommit"
+    enum="NQEEffectiveConnectionType">
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the effective connection type when the optimization guide hint cache
+    has a loaded hint entry matching a URL's host at commit time. This is
+    recorded regardless of whether an associated preview type is allowed for the
+    navigation or not. If no associated preview type is allowed, the hint will
+    not be loaded from a backing store, so this will only capture matches for
+    in-memory hints.
+  </summary>
+</histogram>
+
+<histogram name="Previews.OptimizationGuide.HintCache.PageMatch.AtCommit"
+    enum="NQEEffectiveConnectionType">
+  <owner>dougarnett@chromium.org</owner>
+  <summary>
+    Records the effective connection type when the optimization guide hint cache
+    has a loaded page hint for a URL at commit time. This is recorded regardless
+    of whether an associated preview type is allowed for the navigation or not.
+    If no associated preview type is allowed, the hint will not be loaded from a
+    backing store, so this will only capture matches for in-memory hints.
+  </summary>
+</histogram>
+
 <histogram name="Previews.OptOut.DBRowCount" units="rows">
   <obsolete>
     No longer used as of 06/2018.
@@ -121610,7 +121760,24 @@
       label="Resources identified by any mime type that did match any
              supported css, html, image, javascript, or video mime types."/>
   <suffix name="Video" label="Resources identified by video/*."/>
+  <affected-histogram name="Ads.ResourceUsage.Size.Cache.Mime"/>
   <affected-histogram name="Ads.ResourceUsage.Size.Mime"/>
+  <affected-histogram name="Ads.ResourceUsage.Size.Network.Mime"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="AdResourceSizes" separator=".">
+  <suffix name="Mainframe.AdResource"
+      label="Mainframe resources tagged as ads."/>
+  <suffix name="Mainframe.VanillaResource"
+      label="Mainframe resources not tagged as ads."/>
+  <suffix base="true" name="Mime"
+      label="Size of ad resources identified by the response header mime
+             type."/>
+  <suffix name="Subframe.AdResource" label="Subframe resources tagged as ads."/>
+  <suffix name="Subframe.VanillaResource"
+      label="Subframe resources not tagged as ads."/>
+  <affected-histogram name="Ads.ResourceUsage.Size.Cache"/>
+  <affected-histogram name="Ads.ResourceUsage.Size.Network"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AdsPageLoadMetrics" separator="." ordering="prefix">
diff --git a/tools/perf/page_sets/data/credentials.json.sha1 b/tools/perf/page_sets/data/credentials.json.sha1
index f4677d0..177cf291 100644
--- a/tools/perf/page_sets/data/credentials.json.sha1
+++ b/tools/perf/page_sets/data/credentials.json.sha1
@@ -1 +1 @@
-62bb07f739452c44b3618b9b3f3de6251bf73322
\ No newline at end of file
+38f6afad3baf96684730bf307244fc7fa6bd6f7d
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index dfa2eff9..5849d951 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -12,6 +12,9 @@
         "browse:media:tumblr": {
             "DEFAULT": "system_health_desktop_036.wprgo"
         },
+        "browse:media:tumblr:2018": {
+            "DEFAULT": "system_health_desktop_55cb9dd713.wprgo"
+        },
         "browse:media:youtube": {
             "DEFAULT": "system_health_desktop_026.wprgo",
             "linux": "system_health_desktop_026.wprgo",
diff --git a/tools/perf/page_sets/data/system_health_desktop_55cb9dd713.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_55cb9dd713.wprgo.sha1
new file mode 100644
index 0000000..7be67b22
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_55cb9dd713.wprgo.sha1
@@ -0,0 +1 @@
+55cb9dd71364f014e51f4ed50207ac91820a555d
\ No newline at end of file
diff --git a/tools/perf/page_sets/login_helpers/tumblr_login.py b/tools/perf/page_sets/login_helpers/tumblr_login.py
new file mode 100644
index 0000000..5eff8533
--- /dev/null
+++ b/tools/perf/page_sets/login_helpers/tumblr_login.py
@@ -0,0 +1,31 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from page_sets.login_helpers import login_utils
+
+
+def LoginDesktopAccount(action_runner, credential,
+                 credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+  """Logs in into a Tumblr account."""
+
+  account_name, password = login_utils.GetAccountNameAndPassword(
+      credential, credentials_path=credentials_path)
+
+  action_runner.Navigate('https://www.tumblr.com/login')
+  login_utils.InputWithSelector(
+      action_runner, account_name, 'input[type=email]')
+
+  next_button = '.signup_determine_btn'
+  enter_password_button = '.forgot_password_link'
+  action_runner.WaitForElement(selector=next_button)
+  action_runner.ClickElement(selector=next_button)
+  action_runner.Wait(1)
+  action_runner.WaitForElement(selector=enter_password_button)
+  action_runner.ClickElement(selector=enter_password_button)
+  action_runner.Wait(1)
+  login_utils.InputWithSelector(
+      action_runner, password, 'input[type=password]')
+  action_runner.Wait(1)
+  action_runner.WaitForElement(selector=next_button)
+  action_runner.ClickElement(selector=next_button)
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index b5886e05..7838b30 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -15,6 +15,7 @@
 
 from page_sets.login_helpers import facebook_login
 from page_sets.login_helpers import pinterest_login
+from page_sets.login_helpers import tumblr_login
 
 from telemetry.util import js_template
 
@@ -592,6 +593,28 @@
     action_runner.MouseClick(selector='#tumblr_lightbox_center_image')
     action_runner.Wait(1)  # To make browsing more realistic.
 
+
+class TumblrDesktopStory2018(_MediaBrowsingStory):
+  NAME = 'browse:media:tumblr:2018'
+  URL = 'https://tumblr.com/search/gifs'
+  ITEM_SELECTOR = '.post_media'
+  IS_SINGLE_PAGE_APP = True
+  ITEMS_TO_VISIT = 8
+  INCREMENT_INDEX_AFTER_EACH_ITEM = True
+  SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
+  TAGS = [story_tags.YEAR_2018]
+
+  def _Login(self, action_runner):
+    tumblr_login.LoginDesktopAccount(action_runner, 'tumblr')
+    action_runner.Wait(3)
+
+  def _ViewMediaItem(self, action_runner, index):
+    super(TumblrDesktopStory2018, self)._ViewMediaItem(action_runner, index)
+    action_runner.WaitForElement(selector='#tumblr_lightbox')
+    action_runner.MouseClick(selector='#tumblr_lightbox')
+    action_runner.Wait(1)  # To make browsing more realistic.
+
+
 class PinterestDesktopStory(_MediaBrowsingStory):
   NAME = 'browse:media:pinterest'
   URL = 'https://pinterest.com'
diff --git a/ui/android/java/res/drawable-hdpi/dropdown_popup_background_down.9.png b/ui/android/java/res/drawable-hdpi/dropdown_popup_background_down.9.png
deleted file mode 100644
index acaa8a76..0000000
--- a/ui/android/java/res/drawable-hdpi/dropdown_popup_background_down.9.png
+++ /dev/null
Binary files differ
diff --git a/ui/android/java/res/drawable-hdpi/dropdown_popup_background_up.9.png b/ui/android/java/res/drawable-hdpi/dropdown_popup_background_up.9.png
deleted file mode 100644
index 6d78622..0000000
--- a/ui/android/java/res/drawable-hdpi/dropdown_popup_background_up.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/popup_bg.9.png b/ui/android/java/res/drawable-hdpi/popup_bg.9.png
similarity index 100%
rename from chrome/android/java/res/drawable-hdpi/popup_bg.9.png
rename to ui/android/java/res/drawable-hdpi/popup_bg.9.png
Binary files differ
diff --git a/ui/android/java/res/drawable-mdpi/dropdown_popup_background_down.9.png b/ui/android/java/res/drawable-mdpi/dropdown_popup_background_down.9.png
deleted file mode 100644
index 761e9042..0000000
--- a/ui/android/java/res/drawable-mdpi/dropdown_popup_background_down.9.png
+++ /dev/null
Binary files differ
diff --git a/ui/android/java/res/drawable-mdpi/dropdown_popup_background_up.9.png b/ui/android/java/res/drawable-mdpi/dropdown_popup_background_up.9.png
deleted file mode 100644
index 9c16993b..0000000
--- a/ui/android/java/res/drawable-mdpi/dropdown_popup_background_up.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/popup_bg.9.png b/ui/android/java/res/drawable-mdpi/popup_bg.9.png
similarity index 100%
rename from chrome/android/java/res/drawable-mdpi/popup_bg.9.png
rename to ui/android/java/res/drawable-mdpi/popup_bg.9.png
Binary files differ
diff --git a/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_down.9.png b/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_down.9.png
deleted file mode 100644
index ac01821e..0000000
--- a/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_down.9.png
+++ /dev/null
Binary files differ
diff --git a/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_up.9.png b/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_up.9.png
deleted file mode 100644
index 22a58985..0000000
--- a/ui/android/java/res/drawable-xhdpi/dropdown_popup_background_up.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/popup_bg.9.png b/ui/android/java/res/drawable-xhdpi/popup_bg.9.png
similarity index 100%
rename from chrome/android/java/res/drawable-xhdpi/popup_bg.9.png
rename to ui/android/java/res/drawable-xhdpi/popup_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/popup_bg.9.png b/ui/android/java/res/drawable-xxhdpi/popup_bg.9.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxhdpi/popup_bg.9.png
rename to ui/android/java/res/drawable-xxhdpi/popup_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/popup_bg.9.png b/ui/android/java/res/drawable-xxxhdpi/popup_bg.9.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxxhdpi/popup_bg.9.png
rename to ui/android/java/res/drawable-xxxhdpi/popup_bg.9.png
Binary files differ
diff --git a/ui/android/java/res/drawable/dropdown_popup_background.xml b/ui/android/java/res/drawable/dropdown_popup_background.xml
deleted file mode 100644
index 71b5271..0000000
--- a/ui/android/java/res/drawable/dropdown_popup_background.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_above_anchor="true"
-        android:drawable="@drawable/dropdown_popup_background_up" />
-    <item android:drawable="@drawable/dropdown_popup_background_down" />
-</selector>
\ No newline at end of file
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index a6cf073..2b9701f 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -8,7 +8,7 @@
 
 <resources xmlns:tools="http://schemas.android.com/tools">
     <style name="DropdownPopupWindow" parent="@android:style/Widget.ListPopupWindow">
-        <item name="android:popupBackground">@drawable/dropdown_popup_background</item>
+        <item name="android:popupBackground">@drawable/popup_bg</item>
     </style>
 
     <!-- Buttons -->
diff --git a/ui/android/java/res/values/dimens.xml b/ui/android/java/res/values/dimens.xml
index 7df5398..e1768773 100644
--- a/ui/android/java/res/values/dimens.xml
+++ b/ui/android/java/res/values/dimens.xml
@@ -19,6 +19,8 @@
     <dimen name="dropdown_item_label_margin">10dp</dimen>
     <dimen name="dropdown_icon_margin">8dp</dimen>
     <dimen name="dropdown_vertical_margin">4dp</dimen>
+    <dimen name="dropdown_elevation">2dp</dimen>
+
     <dimen name="button_compat_corner_radius">4dp</dimen>
 
     <!-- Divider Dimensions -->
diff --git a/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java b/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
index ed573c5..4b98a38 100644
--- a/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
+++ b/ui/android/java/src/org/chromium/ui/ContactsPickerListener.java
@@ -4,6 +4,11 @@
 
 package org.chromium.ui;
 
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The callback used to indicate what action the user took in the picker.
  */
@@ -11,11 +16,15 @@
     /**
      * The action the user took in the picker.
      */
-    enum ContactsPickerAction {
-        CANCEL,
-        CONTACTS_SELECTED,
-        SELECT_ALL,
-        UNDO_SELECT_ALL,
+    @IntDef({ContactsPickerAction.CANCEL, ContactsPickerAction.CONTACTS_SELECTED,
+            ContactsPickerAction.SELECT_ALL, ContactsPickerAction.UNDO_SELECT_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ContactsPickerAction {
+        int CANCEL = 0;
+        int CONTACTS_SELECTED = 1;
+        int SELECT_ALL = 2;
+        int UNDO_SELECT_ALL = 3;
+        int NUM_ENTRIES = 4;
     }
 
     /**
@@ -23,5 +32,5 @@
      *
      * @param contacts The contacts that were selected (string contains json format).
      */
-    void onContactsPickerUserAction(ContactsPickerAction action, String contacts);
+    void onContactsPickerUserAction(@ContactsPickerAction int action, String contacts);
 }
diff --git a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
index 2f0465f..b24a8b57 100644
--- a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
+++ b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
@@ -82,12 +82,14 @@
 
         ViewRectProvider rectProvider = new ViewRectProvider(mAnchorView);
         rectProvider.setIncludePadding(true);
-        mBackground = ApiCompatibilityUtils.getDrawable(
-                context.getResources(), R.drawable.dropdown_popup_background);
+        mBackground =
+                ApiCompatibilityUtils.getDrawable(context.getResources(), R.drawable.popup_bg);
         mAnchoredPopupWindow = new AnchoredPopupWindow(
                 context, mAnchorView, mBackground, mContentView, rectProvider);
         mAnchoredPopupWindow.addOnDismissListener(onDismissLitener);
         mAnchoredPopupWindow.setLayoutObserver(this);
+        mAnchoredPopupWindow.setElevation(
+                context.getResources().getDimensionPixelSize(R.dimen.dropdown_elevation));
         Rect paddingRect = new Rect();
         mBackground.getPadding(paddingRect);
         rectProvider.setInsetPx(0, /* top= */ paddingRect.bottom, 0, /* bottom= */ paddingRect.top);
@@ -115,11 +117,8 @@
     public void onPreLayoutChange(
             boolean positionBelow, int x, int y, int width, int height, Rect anchorRect) {
         mBackground.setBounds(anchorRect);
-        mAnchoredPopupWindow.setBackgroundDrawable(positionBelow
-                        ? ApiCompatibilityUtils.getDrawable(mContext.getResources(),
-                                  R.drawable.dropdown_popup_background_down)
-                        : ApiCompatibilityUtils.getDrawable(mContext.getResources(),
-                                  R.drawable.dropdown_popup_background_up));
+        mAnchoredPopupWindow.setBackgroundDrawable(
+                ApiCompatibilityUtils.getDrawable(mContext.getResources(), R.drawable.popup_bg));
     }
 
     /**
diff --git a/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java b/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
index 1bc82eb..7dbc7d849 100644
--- a/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
+++ b/ui/android/java/src/org/chromium/ui/PhotoPickerListener.java
@@ -4,6 +4,11 @@
 
 package org.chromium.ui;
 
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The callback used to indicate what action the user took in the picker.
  */
@@ -11,11 +16,15 @@
     /**
      * The action the user took in the picker.
      */
-    enum Action {
-        CANCEL,
-        PHOTOS_SELECTED,
-        LAUNCH_CAMERA,
-        LAUNCH_GALLERY,
+    @IntDef({PhotoPickerAction.CANCEL, PhotoPickerAction.PHOTOS_SELECTED,
+            PhotoPickerAction.LAUNCH_CAMERA, PhotoPickerAction.LAUNCH_GALLERY})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PhotoPickerAction {
+        int CANCEL = 0;
+        int PHOTOS_SELECTED = 1;
+        int LAUNCH_CAMERA = 2;
+        int LAUNCH_GALLERY = 3;
+        int NUM_ENTRIES = 4;
     }
 
     /**
@@ -29,5 +38,5 @@
      *
      * @param photos The photos that were selected.
      */
-    void onPhotoPickerUserAction(Action action, String[] photos);
+    void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos);
 }
diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
index bbce4492..4ae79ed 100644
--- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
+++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
@@ -373,13 +373,13 @@
     }
 
     @Override
-    public void onPhotoPickerUserAction(Action action, String[] photos) {
+    public void onPhotoPickerUserAction(@PhotoPickerAction int action, String[] photos) {
         switch (action) {
-            case CANCEL:
+            case PhotoPickerAction.CANCEL:
                 onFileNotSelected();
                 break;
 
-            case PHOTOS_SELECTED:
+            case PhotoPickerAction.PHOTOS_SELECTED:
                 if (photos.length == 0) {
                     onFileNotSelected();
                     return;
@@ -402,7 +402,7 @@
                 }
                 break;
 
-            case LAUNCH_GALLERY:
+            case PhotoPickerAction.LAUNCH_GALLERY:
                 Intent intent = new Intent();
                 intent.setType("image/*");
                 if (mAllowMultiple) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
@@ -410,7 +410,7 @@
                 mWindowAndroid.showCancelableIntent(intent, this, R.string.low_memory_error);
                 break;
 
-            case LAUNCH_CAMERA:
+            case PhotoPickerAction.LAUNCH_CAMERA:
                 if (!mWindowAndroid.hasPermission(Manifest.permission.CAMERA)) {
                     mWindowAndroid.requestPermissions(new String[] {Manifest.permission.CAMERA},
                             (permissions, grantResults) -> {
@@ -431,18 +431,18 @@
     }
 
     @Override
-    public void onContactsPickerUserAction(ContactsPickerAction action, String contacts) {
+    public void onContactsPickerUserAction(@ContactsPickerAction int action, String contacts) {
         switch (action) {
-            case CANCEL:
+            case ContactsPickerAction.CANCEL:
                 onFileNotSelected();
                 break;
 
-            case CONTACTS_SELECTED:
+            case ContactsPickerAction.CONTACTS_SELECTED:
                 nativeOnContactsSelected(mNativeSelectFileDialog, contacts);
                 break;
 
-            case SELECT_ALL:
-            case UNDO_SELECT_ALL:
+            case ContactsPickerAction.SELECT_ALL:
+            case ContactsPickerAction.UNDO_SELECT_ALL:
                 break;
         }
     }
diff --git a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
index 14d2a38..bad9968 100644
--- a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
+++ b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
@@ -20,6 +20,7 @@
 import android.widget.PopupWindow;
 import android.widget.PopupWindow.OnDismissListener;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.base.VisibleForTesting;
 
@@ -344,6 +345,13 @@
         mPopupWindow.setBackgroundDrawable(background);
     }
 
+    /**
+     * Sets the elevation of the popup, if elevation is supported.
+     */
+    public void setElevation(float elevation) {
+        ApiCompatibilityUtils.setElevation(mPopupWindow, elevation);
+    }
+
     // RectProvider.Observer implementation.
     @Override
     public void onRectChanged() {
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index b7d2daf..1e2170e 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -28,7 +28,7 @@
 #
 # Introduce a standalone target that can build on both 'host' and 'target'
 # toolchains that just builds the support to load datapacks. The dependencies
-# should be kept minimal to have to build too many targets with multiple
+# should be kept minimal to not have to build too many targets with multiple
 # toolchains.
 component("ui_data_pack") {
   sources = [
@@ -46,8 +46,11 @@
 
   defines = [ "UI_DATA_PACK_IMPLEMENTATION" ]
 
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+  configs += [
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//build/config/compiler:wexit_time_destructors",
+  ]
 }
 
 buildflag_header("ui_features") {
@@ -399,8 +402,11 @@
     ]
   }
 
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+  configs += [
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//build/config/compiler:wexit_time_destructors",
+  ]
 
   defines = [ "UI_BASE_IMPLEMENTATION" ]
 
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 2a00d4e8..71a367e0 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -750,7 +750,7 @@
 void ResourceBundle::InitSharedInstance(Delegate* delegate) {
   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
   g_shared_instance_ = new ResourceBundle(delegate);
-  static std::vector<ScaleFactor> supported_scale_factors;
+  std::vector<ScaleFactor> supported_scale_factors;
 #if defined(OS_IOS)
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
   if (display.device_scale_factor() > 2.0) {
diff --git a/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
index 9ffc036..e029cafe 100644
--- a/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
+++ b/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc
@@ -140,7 +140,7 @@
 #endif  // !defined(JCS_EXTENSIONS)
 
 // jpeg_decompress_struct Deleter.
-struct JpegDecompressStructDeleter {
+struct JpegRobustDecompressStructDeleter {
   void operator()(jpeg_decompress_struct* ptr) {
     jpeg_destroy_decompress(ptr);
     delete ptr;
@@ -155,8 +155,8 @@
                                  std::vector<unsigned char>* output,
                                  int* w,
                                  int* h) {
-  std::unique_ptr<jpeg_decompress_struct, JpegDecompressStructDeleter> cinfo(
-      new jpeg_decompress_struct);
+  std::unique_ptr<jpeg_decompress_struct, JpegRobustDecompressStructDeleter>
+      cinfo(new jpeg_decompress_struct);
   output->clear();
 
   // We set up the normal JPEG error routines, then override error_exit.
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index fed57e1..b268e3d 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -194,6 +194,8 @@
 }
 
 void GLContext::BackpressureFenceWait(uint64_t fence) {}
+
+void GLContext::FlushForDriverCrashWorkaround() {}
 #endif
 
 bool GLContext::HasExtension(const char* name) {
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h
index 9224532..9e38ea80 100644
--- a/ui/gl/gl_context.h
+++ b/ui/gl/gl_context.h
@@ -202,6 +202,9 @@
   virtual uint64_t BackpressureFenceCreate();
   // Perform a client-side wait on a previously-created fence.
   virtual void BackpressureFenceWait(uint64_t fence);
+  // Flush the underlying context to avoid crashes due to driver bugs on macOS.
+  // https://crbug.com/863817
+  virtual void FlushForDriverCrashWorkaround();
 #endif
 
  protected:
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index fd9d85f..a563920e 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -229,8 +229,8 @@
 uint64_t GLContextCGL::BackpressureFenceCreate() {
   TRACE_EVENT0("gpu", "GLContextCGL::BackpressureFenceCreate");
 
-  // This flush may trigger a crash due to driver instability.
-  // https://crbug.com/863817
+  // This flush will trigger a crash if FlushForDriverCrashWorkaround is not
+  // called sufficiently frequently.
   glFlush();
 
   if (gl::GLFence::IsSupported()) {
@@ -287,6 +287,12 @@
     backpressure_fences_.erase(backpressure_fences_.begin());
 }
 
+void GLContextCGL::FlushForDriverCrashWorkaround() {
+  if (!context_ || CGLGetCurrentContext() != context_)
+    return;
+  glFlush();
+}
+
 bool GLContextCGL::MakeCurrent(GLSurface* surface) {
   DCHECK(context_);
 
diff --git a/ui/gl/gl_context_cgl.h b/ui/gl/gl_context_cgl.h
index 6e3c4b1..c0aa656a 100644
--- a/ui/gl/gl_context_cgl.h
+++ b/ui/gl/gl_context_cgl.h
@@ -38,6 +38,7 @@
       const gfx::ColorSpace& color_space) override;
   uint64_t BackpressureFenceCreate() override;
   void BackpressureFenceWait(uint64_t fence) override;
+  void FlushForDriverCrashWorkaround() override;
 
  protected:
   ~GLContextCGL() override;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 3a9ab8c9..2ff6033 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -151,6 +151,14 @@
       return shift
                  ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
                  : ui::TextEditCommand::MOVE_TO_END_OF_LINE;
+    case ui::VKEY_UP:
+      return shift ? ui::TextEditCommand::
+                         MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
+                   : ui::TextEditCommand::INVALID_COMMAND;
+    case ui::VKEY_DOWN:
+      return shift
+                 ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
+                 : ui::TextEditCommand::INVALID_COMMAND;
     case ui::VKEY_BACK:
       if (!control)
         return ui::TextEditCommand::DELETE_BACKWARD;
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 65f47759..be57d65 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -1067,27 +1067,18 @@
 
   textfield_->SetSelectionRange(gfx::Range(6));
 
-  // Shift+[Up/Down] on Mac should execute the command
-  // MOVE_[UP/DOWN]_AND_MODIFY_SELECTION. On other platforms, textfield won't
-  // handle these events.
+  // Shift+[Up/Down] should select the text to the beginning and end of the
+  // line, respectively.
   SendKeyEvent(ui::VKEY_UP, true /* shift */, false /* command */);
   EXPECT_TRUE(textfield_->key_received());
-#if defined(OS_MACOSX)
   EXPECT_TRUE(textfield_->key_handled());
   EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange());
-#else
-  EXPECT_FALSE(textfield_->key_handled());
-#endif
   textfield_->clear();
 
   SendKeyEvent(ui::VKEY_DOWN, true /* shift */, false /* command */);
   EXPECT_TRUE(textfield_->key_received());
-#if defined(OS_MACOSX)
   EXPECT_TRUE(textfield_->key_handled());
   EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange());
-#else
-  EXPECT_FALSE(textfield_->key_handled());
-#endif
   textfield_->clear();
 }