diff --git a/DEPS b/DEPS
index c9845b4..c90f66a 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,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': 'e178c0586cd6daadb7671d6fd53642c25b464d2a',
+  'skia_revision': 'bc94e79eb7b6c0f370101012199c4753a336cc18',
   # 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': 'c3ba2e82ba6ffaded66173ff8babb6f500c90746',
+  'v8_revision': '0a278d064c61f6f482db698cd2c8096850991247',
   # 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.
@@ -145,11 +145,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'a32d6303a613c170ea5795669a2161e70e1d7678',
+  'swiftshader_revision': '28f142f1b0a2ec18304ec2f11518f72747ac6d11',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8bceb48c222230c21aada35281b68746d9fffc46',
+  'pdfium_revision': 'ae6ebf06c7e85453b91ccd489a6715eb5f013e1b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -196,7 +196,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': 'ffa3433a8c084d5cab2a04a603ec5d396292886c',
+  'catapult_revision': 'd235eb23657332468da6ceeb78789a4bd693fda6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -1395,7 +1395,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6130f7f5dc8d6b98dcd1bb6841e60f3de0dec92',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c0a0741eb59828931b200197ef026715d30d7957',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/public/cpp/shelf_item_delegate.cc b/ash/public/cpp/shelf_item_delegate.cc
index 8eaeca1..1bd8e9ad 100644
--- a/ash/public/cpp/shelf_item_delegate.cc
+++ b/ash/public/cpp/shelf_item_delegate.cc
@@ -19,6 +19,9 @@
 ShelfItemDelegate::~ShelfItemDelegate() = default;
 
 mojom::ShelfItemDelegatePtr ShelfItemDelegate::CreateInterfacePtrAndBind() {
+  if (binding_.is_bound())
+    binding_.Close();
+
   mojom::ShelfItemDelegatePtr ptr;
   binding_.Bind(mojo::MakeRequest(&ptr));
   return ptr;
diff --git a/ash/public/cpp/shelf_model.cc b/ash/public/cpp/shelf_model.cc
index 9fee37d..b740d05 100644
--- a/ash/public/cpp/shelf_model.cc
+++ b/ash/public/cpp/shelf_model.cc
@@ -5,6 +5,7 @@
 #include "ash/public/cpp/shelf_model.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model_observer.h"
@@ -144,6 +145,18 @@
     observer.ShelfItemRemoved(index, old_item);
 }
 
+std::unique_ptr<ShelfItemDelegate>
+ShelfModel::RemoveItemAndTakeShelfItemDelegate(const ShelfID& shelf_id) {
+  const int index = ItemIndexByID(shelf_id);
+  if (index < 0)
+    return nullptr;
+
+  auto it = id_to_item_delegate_map_.find(shelf_id);
+  std::unique_ptr<ShelfItemDelegate> item = std::move(it->second);
+  RemoveItemAt(index);
+  return item;
+}
+
 void ShelfModel::Move(int index, int target_index) {
   if (index == target_index)
     return;
diff --git a/ash/public/cpp/shelf_model.h b/ash/public/cpp/shelf_model.h
index fd438acfc..d2ec5d2 100644
--- a/ash/public/cpp/shelf_model.h
+++ b/ash/public/cpp/shelf_model.h
@@ -58,6 +58,12 @@
   // Removes the item at |index|.
   void RemoveItemAt(int index);
 
+  // Removes the item with id |shelf_id| and passes ownership of its
+  // ShelfItemDelegate to the caller. This is useful if you want to remove an
+  // item from the shelf temporarily and be able to restore its behavior later.
+  std::unique_ptr<ShelfItemDelegate> RemoveItemAndTakeShelfItemDelegate(
+      const ShelfID& shelf_id);
+
   // Moves the item at |index| to |target_index|. |target_index| is in terms
   // of the model *after* the item at |index| is removed.
   void Move(int index, int target_index);
diff --git a/ash/public/cpp/shelf_model_unittest.cc b/ash/public/cpp/shelf_model_unittest.cc
index 29fe0fd..0fb8616 100644
--- a/ash/public/cpp/shelf_model_unittest.cc
+++ b/ash/public/cpp/shelf_model_unittest.cc
@@ -7,6 +7,7 @@
 #include <set>
 #include <string>
 
+#include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model_observer.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -28,7 +29,9 @@
     AddToResult("removed=%d", removed_count_, &result);
     AddToResult("changed=%d", changed_count_, &result);
     AddToResult("moved=%d", moved_count_, &result);
-    added_count_ = removed_count_ = changed_count_ = moved_count_ = 0;
+    AddToResult("delegate_changed=%d", delegate_changed_count_, &result);
+    added_count_ = removed_count_ = changed_count_ = moved_count_ =
+        delegate_changed_count_ = 0;
     return result;
   }
 
@@ -37,6 +40,11 @@
   void ShelfItemRemoved(int, const ShelfItem&) override { removed_count_++; }
   void ShelfItemChanged(int, const ShelfItem&) override { changed_count_++; }
   void ShelfItemMoved(int, int) override { moved_count_++; }
+  void ShelfItemDelegateChanged(const ShelfID&,
+                                ShelfItemDelegate*,
+                                ShelfItemDelegate*) override {
+    delegate_changed_count_++;
+  }
 
  private:
   void AddToResult(const std::string& format, int count, std::string* result) {
@@ -51,10 +59,27 @@
   int removed_count_ = 0;
   int changed_count_ = 0;
   int moved_count_ = 0;
+  int delegate_changed_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver);
 };
 
+class TestShelfItemDelegate : public ShelfItemDelegate {
+ public:
+  TestShelfItemDelegate(const ShelfID& shelf_id)
+      : ShelfItemDelegate(shelf_id) {}
+
+  void ItemSelected(std::unique_ptr<ui::Event> event,
+                    int64_t display_id,
+                    ash::ShelfLaunchSource source,
+                    ItemSelectedCallback callback) override {}
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override {}
+  void Close() override {}
+};
+
 }  // namespace
 
 class ShelfModelTest : public testing::Test {
@@ -509,4 +534,32 @@
   EXPECT_FALSE(model_->items()[index].has_notification);
 }
 
+// Test that RemoveItemAndTakeShelfItemDelegate has the same effect as
+// RemoveItemAt and returns the correct delegate.
+TEST_F(ShelfModelTest, RemoveItemAndTakeShelfItemDelegate) {
+  // Add an item.
+  ShelfItem item1;
+  item1.id = ShelfID("item1");
+  item1.type = TYPE_PINNED_APP;
+  model_->Add(item1);
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_LE(0, model_->ItemIndexByID(item1.id));
+  EXPECT_NE(model_->items().end(), model_->ItemByID(item1.id));
+  EXPECT_EQ("added=1", observer_->StateStringAndClear());
+
+  // Set item delegate.
+  auto* delegate = new TestShelfItemDelegate(item1.id);
+  model_->SetShelfItemDelegate(item1.id,
+                               std::unique_ptr<ShelfItemDelegate>(delegate));
+  EXPECT_EQ("delegate_changed=1", observer_->StateStringAndClear());
+
+  // Remove the item.
+  auto taken_delegate = model_->RemoveItemAndTakeShelfItemDelegate(item1.id);
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_EQ(-1, model_->ItemIndexByID(item1.id));
+  EXPECT_EQ(model_->items().end(), model_->ItemByID(item1.id));
+  EXPECT_EQ("removed=1", observer_->StateStringAndClear());
+  EXPECT_EQ(delegate, taken_delegate.get());
+}
+
 }  // namespace ash
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 6a739f6..fab6fe1ab 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8915089885321306400
\ No newline at end of file
+8914872956664439568
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 280a06d..ee8554c 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8915114915290958432
\ No newline at end of file
+8914885528414328080
\ No newline at end of file
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 0ce237fe13..15e385e6 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -254,7 +254,7 @@
         sdk_redist_root_version = os.path.join(sdk_redist_root, directory)
         if not os.path.isdir(sdk_redist_root_version):
           continue
-        if re.match('10\.\d+\.\d+\.\d+', directory):
+        if re.match(r'10\.\d+\.\d+\.\d+', directory):
           source_dir = os.path.join(sdk_redist_root_version, target_cpu, 'ucrt')
           break
     _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
@@ -278,7 +278,7 @@
   for directory in vc_component_msvc_contents:
     if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)):
       continue
-    if re.match('14\.\d+\.\d+', directory):
+    if re.match(r'14\.\d+\.\d+', directory):
       return os.path.join(vc_component_msvc_root, directory)
   raise Exception('Unable to find the VC %s directory.' % component)
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 08f391c..5fdb5876 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=76
 MINOR=0
-BUILD=3778
+BUILD=3781
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 1ca1ada..a2224bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -15,6 +15,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
 import org.chromium.base.SysUtils;
+import org.chromium.base.TimeUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.metrics.RecordUserAction;
@@ -116,8 +117,6 @@
     // an existing tap-selection.
     private static final int TAP_ON_TAP_SELECTION_DELAY_MS = 100;
 
-    private static final int NANOSECONDS_IN_A_MILLISECOND = 1000000;
-
     private final ObserverList<ContextualSearchObserver> mObservers =
             new ObserverList<ContextualSearchObserver>();
 
@@ -1419,7 +1418,8 @@
                 && tapTimeNanoseconds > 0) {
             delayBeforeFinishingWorkMs = ContextualSearchFieldTrial.getValue(
                                                  ContextualSearchSetting.WAIT_AFTER_TAP_DELAY_MS)
-                    - (System.nanoTime() - tapTimeNanoseconds) / NANOSECONDS_IN_A_MILLISECOND;
+                    - (System.nanoTime() - tapTimeNanoseconds)
+                            / TimeUtils.NANOSECONDS_PER_MILLISECOND;
         }
 
         // Finish work on the current state, either immediately or with a delay.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
index d7fabdd..83a8f9a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
@@ -372,23 +372,23 @@
 
         switch (DownloadFilter.fromMimeType(downloadInfo.getMimeType())) {
             case DownloadFilter.Type.PAGE:
-                offlineItem.filter = OfflineItemFilter.FILTER_PAGE;
+                offlineItem.filter = OfflineItemFilter.PAGE;
                 break;
             case DownloadFilter.Type.VIDEO:
-                offlineItem.filter = OfflineItemFilter.FILTER_VIDEO;
+                offlineItem.filter = OfflineItemFilter.VIDEO;
                 break;
             case DownloadFilter.Type.AUDIO:
-                offlineItem.filter = OfflineItemFilter.FILTER_AUDIO;
+                offlineItem.filter = OfflineItemFilter.AUDIO;
                 break;
             case DownloadFilter.Type.IMAGE:
-                offlineItem.filter = OfflineItemFilter.FILTER_IMAGE;
+                offlineItem.filter = OfflineItemFilter.IMAGE;
                 break;
             case DownloadFilter.Type.DOCUMENT:
-                offlineItem.filter = OfflineItemFilter.FILTER_DOCUMENT;
+                offlineItem.filter = OfflineItemFilter.DOCUMENT;
                 break;
             case DownloadFilter.Type.OTHER:
             default:
-                offlineItem.filter = OfflineItemFilter.FILTER_OTHER;
+                offlineItem.filter = OfflineItemFilter.OTHER;
                 break;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
index 6bc1d95..e97db96 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
@@ -63,16 +63,16 @@
      */
     public static @FilterType Integer fromOfflineItem(@OfflineItemFilter int filter) {
         switch (filter) {
-            case OfflineItemFilter.FILTER_PAGE:
+            case OfflineItemFilter.PAGE:
                 return FilterType.SITES;
-            case OfflineItemFilter.FILTER_VIDEO:
+            case OfflineItemFilter.VIDEO:
                 return FilterType.VIDEOS;
-            case OfflineItemFilter.FILTER_AUDIO:
+            case OfflineItemFilter.AUDIO:
                 return FilterType.MUSIC;
-            case OfflineItemFilter.FILTER_IMAGE:
+            case OfflineItemFilter.IMAGE:
                 return FilterType.IMAGES;
-            // case OfflineItemFilter.FILTER_OTHER
-            // case OfflineItemFilter.FILTER_DOCUMENT
+            // case OfflineItemFilter.OTHER
+            // case OfflineItemFilter.DOCUMENT
             default:
                 return FilterType.OTHER;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java
index 70a9c91c..0550da7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutator.java
@@ -185,7 +185,7 @@
                             sectionIndex == 0 && dateIndex > 0 /* showDivider */);
                     if (!mHideSectionHeaders) {
                         sectionHeaderItem.showTitle = !mHideTitleFromSectionHeaders;
-                        sectionHeaderItem.showMenu = filter == OfflineItemFilter.FILTER_IMAGE;
+                        sectionHeaderItem.showMenu = filter == OfflineItemFilter.IMAGE;
                         sectionHeaderItem.items = new ArrayList<>(section.items.values());
                     }
                     listItems.add(sectionHeaderItem);
@@ -195,7 +195,7 @@
                 for (OfflineItem offlineItem : section.items.values()) {
                     OfflineItemListItem item = new OfflineItemListItem(offlineItem);
                     if (mConfig.supportFullWidthImages && section.items.size() == 1
-                            && offlineItem.filter == OfflineItemFilter.FILTER_IMAGE) {
+                            && offlineItem.filter == OfflineItemFilter.IMAGE) {
                         item.spanFullWidth = true;
                     }
                     listItems.add(item);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
index 4062b694..0b511fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
@@ -93,16 +93,16 @@
             if (offlineItem.item.isSuggested) return ViewType.PREFETCH;
 
             switch (offlineItem.item.filter) {
-                case OfflineItemFilter.FILTER_VIDEO:
+                case OfflineItemFilter.VIDEO:
                     return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO;
-                case OfflineItemFilter.FILTER_IMAGE:
+                case OfflineItemFilter.IMAGE:
                     return inProgress ? ViewType.IN_PROGRESS_IMAGE
                                       : (offlineItem.spanFullWidth ? ViewType.IMAGE_FULL_WIDTH
                                                                    : ViewType.IMAGE);
-                // case OfflineItemFilter.FILTER_PAGE:
-                // case OfflineItemFilter.FILTER_AUDIO:
-                // case OfflineItemFilter.FILTER_OTHER:
-                // case OfflineItemFilter.FILTER_DOCUMENT:
+                // case OfflineItemFilter.PAGE:
+                // case OfflineItemFilter.AUDIO:
+                // case OfflineItemFilter.OTHER:
+                // case OfflineItemFilter.DOCUMENT:
                 default:
                     return inProgress ? ViewType.IN_PROGRESS : ViewType.GENERIC;
             }
@@ -117,17 +117,17 @@
      */
     public static @StringRes int getTextForSection(int filter) {
         switch (filter) {
-            case OfflineItemFilter.FILTER_PAGE:
+            case OfflineItemFilter.PAGE:
                 return R.string.download_manager_ui_pages;
-            case OfflineItemFilter.FILTER_IMAGE:
+            case OfflineItemFilter.IMAGE:
                 return R.string.download_manager_ui_images;
-            case OfflineItemFilter.FILTER_VIDEO:
+            case OfflineItemFilter.VIDEO:
                 return R.string.download_manager_ui_video;
-            case OfflineItemFilter.FILTER_AUDIO:
+            case OfflineItemFilter.AUDIO:
                 return R.string.download_manager_ui_audio;
-            case OfflineItemFilter.FILTER_OTHER:
+            case OfflineItemFilter.OTHER:
                 return R.string.download_manager_ui_other;
-            case OfflineItemFilter.FILTER_DOCUMENT:
+            case OfflineItemFilter.DOCUMENT:
                 return R.string.download_manager_ui_documents;
             default:
                 return R.string.download_manager_ui_all_downloads;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
index 4666b99..48296908 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
@@ -168,9 +168,9 @@
     /** @return Whether or not {@code item} can show a thumbnail in the UI. */
     public static boolean canHaveThumbnails(OfflineItem item) {
         switch (item.filter) {
-            case OfflineItemFilter.FILTER_PAGE:
-            case OfflineItemFilter.FILTER_VIDEO:
-            case OfflineItemFilter.FILTER_IMAGE:
+            case OfflineItemFilter.PAGE:
+            case OfflineItemFilter.VIDEO:
+            case OfflineItemFilter.IMAGE:
                 return true;
             default:
                 return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
index 40b3d45..8205c03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
@@ -212,7 +212,7 @@
      */
     public static void recordItemsShared(Collection<OfflineItem> items) {
         for (OfflineItem item : items) {
-            if (item.filter == OfflineItemFilter.FILTER_PAGE) {
+            if (item.filter == OfflineItemFilter.PAGE) {
                 RecordUserAction.record("OfflinePages.Sharing.SharePageFromDownloadHome");
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index e6f620f..12bffbe2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -540,7 +540,7 @@
 
         @Override
         public boolean isOfflinePage() {
-            return mItem.filter == OfflineItemFilter.FILTER_PAGE;
+            return mItem.filter == OfflineItemFilter.PAGE;
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
index 99d896d..5029bc55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -8,9 +8,13 @@
 import android.support.annotation.IntDef;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationSet;
 import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
 import android.view.animation.Transformation;
 
 import org.chromium.base.metrics.RecordHistogram;
@@ -68,13 +72,14 @@
 
     private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
 
-    private static final int SCALE_DOWN_DURATION_MS = 500;
+    private static final int SCALE_DOWN_DURATION_MS = 400;
     private static final int ANIMATE_TO_START_DURATION_MS = 500;
 
     // Minimum number of pull updates necessary to trigger a side nav.
     private static final int MIN_PULLS_TO_ACTIVATE = 3;
 
     private final DecelerateInterpolator mDecelerateInterpolator;
+    private final LinearInterpolator mLinearInterpolator;
     private final float mTotalDragDistance;
     private final int mMediumAnimationDuration;
     private final int mCircleWidth;
@@ -101,7 +106,8 @@
     private int mFrom;
     private int mOriginalOffset;
 
-    private Animation mScaleDownAnimation;
+    private AnimationSet mHidingAnimation;
+    private int mAnimationViewWidth;
     private AnimationListener mCancelAnimationListener;
 
     private boolean mIsForward;
@@ -116,8 +122,8 @@
 
         @Override
         public void onAnimationEnd(Animation animation) {
+            mArrowView.setVisibility(View.INVISIBLE);
             if (mNavigating) {
-                // Make sure the arrow widget is fully visible
                 if (mListener != null) mListener.onNavigate(mIsForward);
                 recordHistogram("Overscroll.Navigated3", mIsForward);
             } else {
@@ -147,6 +153,7 @@
 
         setWillNotDraw(false);
         mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
+        mLinearInterpolator = new LinearInterpolator();
 
         final float density = getResources().getDisplayMetrics().density;
         mCircleWidth = (int) (CIRCLE_DIAMETER_DP * density);
@@ -183,7 +190,7 @@
     private void setNavigating(boolean navigating) {
         if (mNavigating != navigating) {
             mNavigating = navigating;
-            if (mNavigating) startScaleDownAnimation(mNavigateListener);
+            if (mNavigating) startHidingAnimation(mNavigateListener);
         }
     }
 
@@ -191,20 +198,25 @@
         return mIsForward ? -Math.min(0, mTotalMotion) : Math.max(0, mTotalMotion);
     }
 
-    private void startScaleDownAnimation(AnimationListener listener) {
-        if (mScaleDownAnimation == null) {
-            mScaleDownAnimation = new Animation() {
-                @Override
-                public void applyTransformation(float interpolatedTime, Transformation t) {
-                    float progress = 1 - interpolatedTime; // [0..1]
-                    mArrowView.setScaleY(progress);
-                }
-            };
-            mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION_MS);
+    private void startHidingAnimation(AnimationListener listener) {
+        // ScaleAnimation needs to be created again if the arrow widget width changes over time
+        // (due to turning on/off close indicator) to set the right x pivot point.
+        if (mHidingAnimation == null || mAnimationViewWidth != mArrowViewWidth) {
+            mAnimationViewWidth = mArrowViewWidth;
+            ScaleAnimation scalingDown =
+                    new ScaleAnimation(1, 0, 1, 0, mArrowViewWidth / 2, mArrowView.getHeight() / 2);
+            scalingDown.setInterpolator(mLinearInterpolator);
+            scalingDown.setDuration(SCALE_DOWN_DURATION_MS);
+            Animation fadingOut = new AlphaAnimation(1, 0);
+            fadingOut.setInterpolator(mDecelerateInterpolator);
+            fadingOut.setDuration(SCALE_DOWN_DURATION_MS);
+            mHidingAnimation = new AnimationSet(false);
+            mHidingAnimation.addAnimation(fadingOut);
+            mHidingAnimation.addAnimation(scalingDown);
         }
         mArrowView.setAnimationListener(listener);
         mArrowView.clearAnimation();
-        mArrowView.startAnimation(mScaleDownAnimation);
+        mArrowView.startAnimation(mHidingAnimation);
     }
 
     /**
@@ -284,7 +296,6 @@
                 * 2f;
 
         if (mArrowView.getVisibility() != View.VISIBLE) mArrowView.setVisibility(View.VISIBLE);
-        mArrowView.setScaleY(1f);
 
         float originalDragPercent = overscroll / mTotalDragDistance;
         float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
@@ -340,7 +351,7 @@
 
                 @Override
                 public void onAnimationEnd(Animation animation) {
-                    startScaleDownAnimation(mNavigateListener);
+                    startHidingAnimation(mNavigateListener);
                 }
 
                 @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
index 508571f..8e7634c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
@@ -407,7 +407,7 @@
         offlineItem.filePath = targetPath;
         offlineItem.creationTimeMs = startTime;
         offlineItem.totalSizeBytes = totalSize;
-        offlineItem.filter = OfflineItemFilter.FILTER_PAGE;
+        offlineItem.filter = OfflineItemFilter.PAGE;
         return offlineItem;
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
index 31dd6156..ecb20fd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
@@ -19,16 +19,16 @@
     @Test
     public void testFilterConversions() {
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.SITES),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE));
+                Filters.fromOfflineItem(OfflineItemFilter.PAGE));
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.VIDEOS),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO));
+                Filters.fromOfflineItem(OfflineItemFilter.VIDEO));
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.MUSIC),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO));
+                Filters.fromOfflineItem(OfflineItemFilter.AUDIO));
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.IMAGES),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE));
+                Filters.fromOfflineItem(OfflineItemFilter.IMAGE));
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER));
+                Filters.fromOfflineItem(OfflineItemFilter.OTHER));
         Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER),
-                Filters.fromOfflineItem(OfflineItemFilter.FILTER_DOCUMENT));
+                Filters.fromOfflineItem(OfflineItemFilter.DOCUMENT));
     }
 }
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
index 63e36bc..6b52b88 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
@@ -39,14 +39,14 @@
 
     @Test
     public void testTypeFiltering() {
-        OfflineItem item1 = buildItem(OfflineItemFilter.FILTER_PAGE, false);
-        OfflineItem item2 = buildItem(OfflineItemFilter.FILTER_VIDEO, false);
-        OfflineItem item3 = buildItem(OfflineItemFilter.FILTER_AUDIO, false);
-        OfflineItem item4 = buildItem(OfflineItemFilter.FILTER_IMAGE, false);
-        OfflineItem item5 = buildItem(OfflineItemFilter.FILTER_OTHER, false);
-        OfflineItem item6 = buildItem(OfflineItemFilter.FILTER_DOCUMENT, false);
-        OfflineItem item7 = buildItem(OfflineItemFilter.FILTER_PAGE, true);
-        OfflineItem item8 = buildItem(OfflineItemFilter.FILTER_VIDEO, true);
+        OfflineItem item1 = buildItem(OfflineItemFilter.PAGE, false);
+        OfflineItem item2 = buildItem(OfflineItemFilter.VIDEO, false);
+        OfflineItem item3 = buildItem(OfflineItemFilter.AUDIO, false);
+        OfflineItem item4 = buildItem(OfflineItemFilter.IMAGE, false);
+        OfflineItem item5 = buildItem(OfflineItemFilter.OTHER, false);
+        OfflineItem item6 = buildItem(OfflineItemFilter.DOCUMENT, false);
+        OfflineItem item7 = buildItem(OfflineItemFilter.PAGE, true);
+        OfflineItem item8 = buildItem(OfflineItemFilter.VIDEO, true);
         Collection<OfflineItem> sourceItems =
                 CollectionUtil.newHashSet(item1, item2, item3, item4, item5, item6, item7, item8);
         when(mSource.getItems()).thenReturn(sourceItems);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
index 090c827..986193f2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
@@ -89,14 +89,13 @@
      */
     @Test
     public void testSingleItem() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1);
     }
 
@@ -109,16 +108,14 @@
      */
     @Test
     public void testTwoItemsSameDay() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(3, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1);
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 1), item2);
     }
@@ -133,19 +130,17 @@
      */
     @Test
     public void testTwoItemsSameDayDifferentSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_AUDIO, false, false);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, false, false);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2);
     }
 
@@ -159,21 +154,19 @@
      */
     @Test
     public void testShowMenuButtonForImageSectionWithoutDate() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_IMAGE);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.IMAGE);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         Assert.assertFalse(((SectionHeaderListItem) mModel.get(0)).showMenu);
 
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_IMAGE, false, false);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, false, false);
         Assert.assertTrue(((SectionHeaderListItem) mModel.get(2)).showMenu);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2);
     }
@@ -188,21 +181,19 @@
      */
     @Test
     public void testShowMenuButtonForImageSectionWithDate() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.FILTER_IMAGE);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_PAGE);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.IMAGE);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.PAGE);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_IMAGE, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, true, false);
         Assert.assertTrue(((SectionHeaderListItem) mModel.get(0)).showMenu);
 
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_PAGE, false, false);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.PAGE, false, false);
         Assert.assertFalse(((SectionHeaderListItem) mModel.get(2)).showMenu);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2);
     }
@@ -215,14 +206,13 @@
      */
     @Test
     public void testSingleItemInJustNowSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         item1.state = OfflineItemState.IN_PROGRESS;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithJustNowProvider();
 
         Assert.assertEquals(2, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1);
     }
 
@@ -236,10 +226,8 @@
      */
     @Test
     public void testMultipleSectionsInJustNowSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO);
         item1.state = OfflineItemState.IN_PROGRESS;
         item2.state = OfflineItemState.IN_PROGRESS;
         item2.completionTimeMs = item2.creationTimeMs;
@@ -247,9 +235,9 @@
         DateOrderedListMutator list = createMutatorWithJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1);
-        assertJustNowSection(mModel.get(2), OfflineItemFilter.FILTER_AUDIO, false, false);
+        assertJustNowSection(mModel.get(2), OfflineItemFilter.AUDIO, false, false);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2);
     }
 
@@ -270,50 +258,46 @@
      */
     @Test
     public void testItemDoesNotMoveOutOfJustNowSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         item1.state = OfflineItemState.PAUSED;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithJustNowProvider();
         mModel.addObserver(mObserver);
 
         Assert.assertEquals(2, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1);
 
         // Resume the download.
-        OfflineItem update1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         update1.state = OfflineItemState.IN_PROGRESS;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update1));
         list.onItemUpdated(item1, update1);
 
         Assert.assertEquals(2, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update1);
 
         // Complete the download.
-        OfflineItem update2 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem update2 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         update2.state = OfflineItemState.COMPLETE;
         update2.completionTimeMs = update2.creationTimeMs;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update2));
         list.onItemUpdated(update1, update2);
 
         Assert.assertEquals(2, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update2);
 
         // Too much time has passed since completion of the download.
-        OfflineItem update3 =
-                buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem update3 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO);
         update3.state = OfflineItemState.COMPLETE;
         update3.completionTimeMs = buildCalendar(2017, 1, 1, 1).getTimeInMillis();
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update3));
         list.onItemUpdated(update2, update3);
 
         Assert.assertEquals(2, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update3);
     }
 
@@ -328,19 +312,17 @@
      */
     @Test
     public void testJustNowSectionWithOtherDates() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.FILTER_AUDIO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO);
         item1.state = OfflineItemState.IN_PROGRESS;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertJustNowSection(mModel.get(0), OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertJustNowSection(mModel.get(0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 2, 1, 1), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_AUDIO, true, true);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, true, true);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2);
     }
 
@@ -355,19 +337,17 @@
      */
     @Test
     public void testTwoItemsDifferentDayMatchHeader() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 0), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 0), OfflineItemFilter.FILTER_AUDIO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 0), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_AUDIO, true, true);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO, true, true);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 0), item2);
     }
 
@@ -380,16 +360,14 @@
      */
     @Test
     public void testTwoItemsSameDayOutOfOrder() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(3, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 5), item2);
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item1);
     }
@@ -405,19 +383,17 @@
      */
     @Test
     public void testTwoItemsDifferentDaySameSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, true);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2);
     }
 
@@ -432,19 +408,17 @@
      */
     @Test
     public void testTwoItemsDifferentDayDifferentSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_PAGE);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.PAGE);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_PAGE, true, true);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.PAGE, true, true);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2);
     }
 
@@ -459,19 +433,17 @@
      */
     @Test
     public void testTwoItemsDifferentDayOutOfOrder() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item2);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, true);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 4), item1);
     }
 
@@ -489,8 +461,7 @@
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
 
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         list.onItemsAdded(CollectionUtil.newArrayList(item1));
 
@@ -517,15 +488,13 @@
      */
     @Test
     public void testAddFirstItemToList() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 1), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 1), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
 
         // Add an item on the same day that will be placed first.
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         list.onItemsAdded(CollectionUtil.newArrayList(item2));
 
@@ -534,8 +503,7 @@
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 1), item1);
 
         // Add an item on an earlier day that will be placed first.
-        OfflineItem item3 =
-                buildItem("3", buildCalendar(2018, 1, 3, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 3, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2, item3));
         list.onItemsAdded(CollectionUtil.newArrayList(item3));
 
@@ -566,15 +534,13 @@
      */
     @Test
     public void testAddLastItemToList() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
 
         // Add an item on the same day that will be placed last.
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         list.onItemsAdded(CollectionUtil.newArrayList(item2));
 
@@ -583,8 +549,7 @@
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 3), item2);
 
         // Add an item on a later day that will be placed last.
-        OfflineItem item3 =
-                buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2, item3));
         list.onItemsAdded(CollectionUtil.newArrayList(item3));
 
@@ -604,8 +569,7 @@
      */
     @Test
     public void testRemoveOnlyItemInList() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
@@ -629,10 +593,8 @@
      */
     @Test
     public void testRemoveFirstItemInListSameDay() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
@@ -641,8 +603,8 @@
         list.onItemsRemoved(CollectionUtil.newArrayList(item1));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2);
     }
 
@@ -659,10 +621,8 @@
      */
     @Test
     public void testRemoveLastItemInListSameDay() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
@@ -671,8 +631,8 @@
         list.onItemsRemoved(CollectionUtil.newArrayList(item2));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item1);
     }
 
@@ -690,10 +650,8 @@
      */
     @Test
     public void testRemoveOnlyItemInSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_IMAGE);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.IMAGE);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
@@ -703,8 +661,8 @@
         list.onItemsRemoved(CollectionUtil.newArrayList(item1));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_IMAGE, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.IMAGE, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2);
     }
 
@@ -723,10 +681,8 @@
      */
     @Test
     public void testRemoveLastItemInListWithMultipleDays() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 3, 3), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 3, 3), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
@@ -735,8 +691,8 @@
         list.onItemsRemoved(CollectionUtil.newArrayList(item2));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 3, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 3, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 3, 3), item1);
     }
 
@@ -759,26 +715,22 @@
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
 
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item3 =
-                buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item4 =
-                buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO);
+        OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems())
                 .thenReturn(CollectionUtil.newArrayList(item1, item2, item3, item4));
         list.onItemsAdded(CollectionUtil.newArrayList(item1, item2, item3, item4));
 
         Assert.assertEquals(6, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 12), item4);
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 10), item3);
-        assertSectionHeader(mModel.get(3), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, true);
+        assertSectionHeader(
+                mModel.get(3), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, true);
         assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 1, 6), item1);
         assertOfflineItem(mModel.get(5), buildCalendar(2018, 1, 1, 4), item2);
     }
@@ -802,32 +754,29 @@
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
         mModel.addObserver(mObserver);
 
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         list.onItemsAdded(CollectionUtil.newArrayList(item1));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1);
 
         // Complete the download.
-        OfflineItem update1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO);
         update1.state = OfflineItemState.COMPLETE;
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(update1));
         list.onItemUpdated(item1, update1);
 
         // Add a new download.
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
         list.onItemsAdded(CollectionUtil.newArrayList(item2));
 
         Assert.assertEquals(3, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), update1);
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item2);
     }
@@ -849,14 +798,10 @@
      */
     @Test
     public void testRemoveMultipleItems() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item3 =
-                buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item4 =
-                buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
+        OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO);
+        OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems())
                 .thenReturn(CollectionUtil.newArrayList(item1, item2, item3, item4));
@@ -867,8 +812,8 @@
         list.onItemsRemoved(CollectionUtil.newArrayList(item2, item3, item4));
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1);
     }
 
@@ -885,8 +830,7 @@
      */
     @Test
     public void testItemUpdatedSameTimestamp() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
@@ -894,13 +838,13 @@
 
         // Update an item with the same timestamp.
         OfflineItem newItem1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1));
         list.onItemUpdated(item1, newItem1);
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), newItem1);
     }
 
@@ -918,10 +862,8 @@
      */
     @Test
     public void testItemUpdatedSameDay() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
@@ -929,13 +871,13 @@
 
         // Update an item with the same timestamp.
         OfflineItem newItem1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.FILTER_VIDEO);
+                buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1, item2));
         list.onItemUpdated(item1, newItem1);
 
         Assert.assertEquals(3, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2);
         assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 3), newItem1);
     }
@@ -955,10 +897,8 @@
      */
     @Test
     public void testItemUpdatedSameDayDifferentSection() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.FILTER_VIDEO);
-        OfflineItem item2 =
-                buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO);
+        OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1, item2));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
@@ -966,16 +906,16 @@
 
         // Update an item with the same timestamp.
         OfflineItem newItem1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.FILTER_IMAGE);
+                buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.IMAGE);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1, item2));
         list.onItemUpdated(item1, newItem1);
 
         Assert.assertEquals(4, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2);
-        assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0),
-                OfflineItemFilter.FILTER_IMAGE, false, false);
+        assertSectionHeader(
+                mModel.get(2), buildCalendar(2018, 1, 1, 0), OfflineItemFilter.IMAGE, false, false);
         assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 3), newItem1);
     }
 
@@ -992,8 +932,7 @@
      */
     @Test
     public void testItemUpdatedDifferentDay() {
-        OfflineItem item1 =
-                buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.FILTER_VIDEO);
+        OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO);
 
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(item1));
         DateOrderedListMutator list = createMutatorWithoutJustNowProvider();
@@ -1001,13 +940,13 @@
 
         // Update an item with the same timestamp.
         OfflineItem newItem1 =
-                buildItem("1", buildCalendar(2018, 1, 2, 6), OfflineItemFilter.FILTER_VIDEO);
+                buildItem("1", buildCalendar(2018, 1, 2, 6), OfflineItemFilter.VIDEO);
         when(mSource.getItems()).thenReturn(CollectionUtil.newArrayList(newItem1));
         list.onItemUpdated(item1, newItem1);
 
         Assert.assertEquals(2, mModel.size());
-        assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0),
-                OfflineItemFilter.FILTER_VIDEO, true, false);
+        assertSectionHeader(
+                mModel.get(0), buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO, true, false);
         assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 6), newItem1);
     }
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e1cac02..6ed6bad0 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3777.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-76.0.3779.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 932ce8ee..0787eb7 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3460,7 +3460,7 @@
     Your app will open when the upgrade is finished. Upgrades can take a few minutes.
   </message>
   <message name="IDS_CROSTINI_SHUT_DOWN_LINUX_MENU_ITEM" desc="Text shown in the context menu for the Linux terminal app, allowing users to shut down the Linux virtual machine.">
-    Shut Down Linux (Beta)
+    Shut down Linux (Beta)
   </message>
   <message name="IDS_CROSTINI_TERMINA_UPDATE_REQUIRED" desc="Text shown in the Crostini update dialog when the VM component needs updating.">
     Linux (Beta) update required
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index c4623747..5c550a91 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3946,6 +3946,14 @@
      FEATURE_VALUE_TYPE(features::kEnterpriseReportingInBrowser)},
 #endif  // !defined(OS_ANDROID)
 
+    {"enable-autofill-do-not-migrate-unsupported-local-cards",
+     flag_descriptions::kEnableAutofillDoNotMigrateUnsupportedLocalCardsName,
+     flag_descriptions::
+         kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillDoNotMigrateUnsupportedLocalCards)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 9f1ccf92..3324eb0 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -110,24 +110,26 @@
   // request to ARC++ container to extract the real data. This logic is
   // isolated inside ArcAppListPrefs and I don't think that anybody else should
   // call mojom RequestAppIcon".
-  if (icon_resource_id.empty()) {
-    auto* app_instance =
-        ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon);
-    if (app_instance) {
-      app_instance->RequestAppIcon(
-          package_name, activity, size_hint_in_px,
-          base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
-      return;
-    }
+  if (app_connection_holder) {
+    if (icon_resource_id.empty()) {
+      auto* app_instance =
+          ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon);
+      if (app_instance) {
+        app_instance->RequestAppIcon(
+            package_name, activity, size_hint_in_px,
+            base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
+        return;
+      }
 
-  } else {
-    auto* app_instance =
-        ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestShortcutIcon);
-    if (app_instance) {
-      app_instance->RequestShortcutIcon(
-          icon_resource_id, size_hint_in_px,
-          base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
-      return;
+    } else {
+      auto* app_instance = ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder,
+                                                       RequestShortcutIcon);
+      if (app_instance) {
+        app_instance->RequestShortcutIcon(
+            icon_resource_id, size_hint_in_px,
+            base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
+        return;
+      }
     }
   }
 
@@ -179,6 +181,13 @@
 }
 
 ArcApps::~ArcApps() {
+  // Clear out any pending icon calls to avoid a CHECK for Mojo callbacks that
+  // have not been run at App Service destruction.
+  for (auto& pending : pending_load_icon_calls_) {
+    std::move(pending).Run(nullptr);
+  }
+  pending_load_icon_calls_.clear();
+
   if (prefs_) {
     prefs_->app_connection_holder()->RemoveObserver(this);
     prefs_->RemoveObserver(this);
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc
index 4f8680c6..18b5611e 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -13,8 +13,10 @@
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "extensions/common/constants.h"
 
@@ -93,8 +95,10 @@
     const GURL& validated_url,
     const GURL& start_url,
     const GURL& manifest_url) {
-  return extensions::BookmarkOrHostedAppInstalled(
-      web_contents->GetBrowserContext(), start_url);
+  return web_app::WebAppProvider::Get(
+             Profile::FromBrowserContext(web_contents->GetBrowserContext()))
+      ->registrar()
+      .IsInstalled(start_url);
 }
 
 void AppBannerManagerDesktop::ShowBannerUi(WebappInstallSource install_source) {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index eb7f566..c671528 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -578,6 +578,8 @@
     "arc/policy/arc_policy_util.h",
     "arc/print/arc_print_service.cc",
     "arc/print/arc_print_service.h",
+    "arc/print_spooler/arc_print_spooler_bridge.cc",
+    "arc/print_spooler/arc_print_spooler_bridge.h",
     "arc/process/arc_process.cc",
     "arc/process/arc_process.h",
     "arc/process/arc_process_service.cc",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 6255b645..75a76d6 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/chromeos/arc/pip/arc_pip_bridge.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h"
 #include "chrome/browser/chromeos/arc/print/arc_print_service.h"
+#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h"
 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
 #include "chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_bridge.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h"
@@ -181,6 +182,7 @@
   ArcPolicyBridge::GetForBrowserContext(profile);
   ArcPowerBridge::GetForBrowserContext(profile);
   ArcPrintService::GetForBrowserContext(profile);
+  ArcPrintSpoolerBridge::GetForBrowserContext(profile);
   ArcProcessService::GetForBrowserContext(profile);
   ArcPropertyBridge::GetForBrowserContext(profile);
   ArcProvisionNotificationService::GetForBrowserContext(profile);
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
index 2f2efe93..ed37626 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
@@ -14,6 +14,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
 #include "chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge.h"
+#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
+#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/platform_keys/key_permissions.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
@@ -24,7 +26,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/arc_prefs.h"
@@ -93,22 +94,21 @@
   bool is_on_certs_changed_called_ = false;
 };
 
-class ArcCertStoreBridgeTest : public InProcessBrowserTest {
+class ArcCertStoreBridgeTest : public chromeos::MixinBasedInProcessBrowserTest {
  protected:
   ArcCertStoreBridgeTest() = default;
 
-  // InProcessBrowserTest:
+  // chromeos::MixinBasedInProcessBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    InProcessBrowserTest::SetUpCommandLine(command_line);
+    chromeos::MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
 
     arc::SetArcAvailableCommandLineForTesting(command_line);
 
-    policy_helper_ =
-        std::make_unique<policy::UserPolicyTestHelper>(kFakeUserName);
-    policy_helper_->Init(
+    policy_helper_ = std::make_unique<policy::UserPolicyTestHelper>(
+        kFakeUserName, &local_policy_server_);
+    policy_helper_->SetPolicy(
         base::DictionaryValue() /* empty mandatory policy */,
         base::DictionaryValue() /* empty recommended policy */);
-    policy_helper_->UpdateCommandLine(command_line);
 
     command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
                                     kFakeUserName);
@@ -129,7 +129,7 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
+    chromeos::MixinBasedInProcessBrowserTest::SetUpOnMainThread();
 
     policy_helper_->WaitForInitialPolicy(browser()->profile());
 
@@ -163,6 +163,7 @@
     // instance in fixture, once), but it should be no op.
     ArcServiceLauncher::Get()->Shutdown();
     chromeos::ProfileHelper::SetAlwaysReturnPrimaryUserForTesting(false);
+    chromeos::MixinBasedInProcessBrowserTest::TearDownOnMainThread();
   }
 
   ArcBridgeService* arc_bridge() {
@@ -192,7 +193,7 @@
     user_policy.SetKey(policy::key::kKeyPermissions,
                        base::Value(key_permissions_policy_str));
 
-    policy_helper_->UpdatePolicy(
+    policy_helper_->SetPolicyAndWait(
         user_policy, base::DictionaryValue() /* empty recommended policy */,
         browser()->profile());
   }
@@ -299,6 +300,7 @@
 
   std::unique_ptr<policy::UserPolicyTestHelper> policy_helper_;
   std::unique_ptr<FakeArcCertStoreInstance> instance_;
+  chromeos::LocalPolicyTestServerMixin local_policy_server_{&mixin_host_};
 
   DISALLOW_COPY_AND_ASSIGN(ArcCertStoreBridgeTest);
 };
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
new file mode 100644
index 0000000..2060e2bd
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "chrome/browser/ui/browser.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "components/arc/session/arc_bridge_service.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace arc {
+namespace {
+
+// Singleton factory for ArcPrintSpoolerBridge.
+class ArcPrintSpoolerBridgeFactory
+    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+          ArcPrintSpoolerBridge, ArcPrintSpoolerBridgeFactory> {
+ public:
+  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+  static constexpr const char* kName = "ArcPrintSpoolerBridgeFactory";
+
+  static ArcPrintSpoolerBridgeFactory* GetInstance() {
+    return base::Singleton<ArcPrintSpoolerBridgeFactory>::get();
+  }
+
+ private:
+  friend base::DefaultSingletonTraits<ArcPrintSpoolerBridgeFactory>;
+  ArcPrintSpoolerBridgeFactory() = default;
+  ~ArcPrintSpoolerBridgeFactory() override = default;
+};
+
+}  // namespace
+
+// static
+ArcPrintSpoolerBridge* ArcPrintSpoolerBridge::GetForBrowserContext(
+    content::BrowserContext* context) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return ArcPrintSpoolerBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcPrintSpoolerBridge::ArcPrintSpoolerBridge(content::BrowserContext* context,
+                                             ArcBridgeService* bridge_service)
+    : profile_(Profile::FromBrowserContext(context)),
+      arc_bridge_service_(bridge_service),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc_bridge_service_->print_spooler()->SetHost(this);
+}
+
+ArcPrintSpoolerBridge::~ArcPrintSpoolerBridge() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc_bridge_service_->print_spooler()->SetHost(nullptr);
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
new file mode 100644
index 0000000..74a2cced
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/arc/common/print_spooler.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class handles print related IPC from the ARC container and allows print
+// jobs to be displayed and managed in Chrome print preview instead of the
+// Android print UI.
+class ArcPrintSpoolerBridge
+    : public KeyedService,
+      public mojom::PrintSpoolerHost {
+ public:
+  // Returns singleton instance for the given BrowserContext,
+  // or nullptr if the browser |context| is not allowed to use ARC.
+  static ArcPrintSpoolerBridge* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  ArcPrintSpoolerBridge(content::BrowserContext* context,
+                        ArcBridgeService* bridge_service);
+  ~ArcPrintSpoolerBridge() override;
+
+  // mojom::PrintSpoolerHost overrides:
+  // TODO(jschettler): Add overrides.
+
+ private:
+  Profile* const profile_;
+
+  ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
+
+  base::WeakPtrFactory<ArcPrintSpoolerBridge> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcPrintSpoolerBridge);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_BRIDGE_H_
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index 33ca6437..b9a341c 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -176,8 +176,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   EXPECT_FALSE(IsAuthEnabled());
 }
@@ -204,8 +204,8 @@
   bedtime_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      bedtime_policy, base::DictionaryValue(), child_profile_);
 
   // Check that auth is disabled, since the bedtime has already started.
   EXPECT_FALSE(IsAuthEnabled());
@@ -218,8 +218,8 @@
   unlock_override_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(unlock_override_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      unlock_override_policy, base::DictionaryValue(), child_profile_);
 
   // Check that the unlock worked and auth is enabled.
   EXPECT_TRUE(IsAuthEnabled());
@@ -255,8 +255,8 @@
   bedtime_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      bedtime_policy, base::DictionaryValue(), child_profile_);
 
   // Check that auth is enable, since the bedtime hasn't started.
   EXPECT_TRUE(IsAuthEnabled());
@@ -270,8 +270,8 @@
   unlock_override_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(unlock_override_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      unlock_override_policy, base::DictionaryValue(), child_profile_);
 
   // Check that the unlock worked and auth is enabled.
   EXPECT_TRUE(IsAuthEnabled());
@@ -321,8 +321,8 @@
   daily_limit_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(daily_limit_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      daily_limit_policy, base::DictionaryValue(), child_profile_);
 
   // Check that auth is enabled at 10 AM with 0 usage time.
   EXPECT_TRUE(IsAuthEnabled());
@@ -341,8 +341,8 @@
   unlock_override_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(unlock_override_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      unlock_override_policy, base::DictionaryValue(), child_profile_);
 
   // Check that the unlock worked and auth is enabled.
   EXPECT_TRUE(IsAuthEnabled());
@@ -389,8 +389,8 @@
   bedtime_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(bedtime_policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      bedtime_policy, base::DictionaryValue(), child_profile_);
 
   // Check that auth is disabled, since the bedtime has already started.
   EXPECT_FALSE(IsAuthEnabled());
@@ -404,8 +404,8 @@
   unlock_override_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(unlock_override_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      unlock_override_policy, base::DictionaryValue(), child_profile_);
 
   // Check that the unlock worked and auth is enabled.
   EXPECT_TRUE(IsAuthEnabled());
@@ -451,8 +451,8 @@
   daily_limit_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(daily_limit_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      daily_limit_policy, base::DictionaryValue(), child_profile_);
 
   // Check that auth is enabled at 10 AM with 0 usage time.
   EXPECT_TRUE(IsAuthEnabled());
@@ -471,8 +471,8 @@
   unlock_override_policy.SetKey(
       policy::key::kUsageTimeLimit,
       base::Value(utils::PolicyToString(policy_content.get())));
-  user_policy_helper()->UpdatePolicy(unlock_override_policy,
-                                     base::DictionaryValue(), child_profile_);
+  user_policy_helper()->SetPolicyAndWait(
+      unlock_override_policy, base::DictionaryValue(), child_profile_);
 
   // Check that the unlock worked and auth is enabled.
   EXPECT_TRUE(IsAuthEnabled());
@@ -535,8 +535,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Iterate over a week checking that the device is locked properly everyday.
   for (int i = 0; i < 7; i++) {
@@ -591,8 +591,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Iterate over a week checking that the device is locked properly
   // every day.
@@ -645,8 +645,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Verify that device is unlocked at 10 AM.
   EXPECT_FALSE(IsLocked());
@@ -682,8 +682,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Verify that device is unlocked at 10 AM.
   EXPECT_FALSE(IsLocked());
@@ -720,8 +720,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Verify that auth is enabled at 10 AM.
   EXPECT_TRUE(IsAuthEnabled());
@@ -772,8 +772,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   // Verify that auth is disabled at 8 AM.
   EXPECT_TRUE(IsAuthEnabled());
@@ -816,8 +816,8 @@
   policy.SetKey(policy::key::kUsageTimeLimit,
                 base::Value(utils::PolicyToString(policy_content.get())));
 
-  user_policy_helper()->UpdatePolicy(policy, base::DictionaryValue(),
-                                     child_profile_);
+  user_policy_helper()->SetPolicyAndWait(policy, base::DictionaryValue(),
+                                         child_profile_);
 
   TestScreenTimeControllerObserver observer;
   ScreenTimeControllerFactory::GetForBrowserContext(child_profile_)
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index e2ac697..b332d3c 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -72,7 +72,7 @@
   ChromeLauncherController* chrome_controller =
       ChromeLauncherController::instance();
   DCHECK(chrome_controller);
-  chrome_controller->GetShelfSpinnerController()->Close(app_id);
+  chrome_controller->GetShelfSpinnerController()->CloseSpinner(app_id);
 }
 
 void OnCrostiniRestarted(Profile* profile,
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 85eae95..65205896 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -13,6 +13,7 @@
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_manager_base.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "ui/keyboard/public/keyboard_switches.h"
@@ -907,7 +908,9 @@
                       TestCase("requestMountSourceFile"),
                       TestCase("requestMountSourceFile").DisableNativeSmb(),
                       TestCase("providerEject"),
-                      TestCase("providerEject").DisableNativeSmb()));
+                      TestCase("providerEject").DisableNativeSmb(),
+                      TestCase("installNewServiceOnline"),
+                      TestCase("installNewServiceOffline").Offline()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     GearMenu, /* gear_menu.js */
@@ -1085,10 +1088,17 @@
     base::ScopedAllowBlockingForTesting allow_blocking;
     const AccountId account_id(
         AccountId::FromUserEmailGaiaId(info.email, info.gaia_id));
+    user_manager::User* user =
+        user_manager::User::CreateRegularUserForTesting(account_id);
+    static_cast<user_manager::UserManagerBase*>(
+        user_manager::UserManager::Get())
+        ->AddUserRecordForTesting(user);
     if (log_in) {
       session_manager::SessionManager::Get()->CreateSession(account_id,
                                                             info.hash, false);
     }
+    chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
+        user, profile());
     user_manager::UserManager::Get()->SaveUserDisplayName(
         account_id, base::UTF8ToUTF16(info.display_name));
     Profile* profile =
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 0e501d03..afa8569 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -539,24 +539,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestVolume);
 };
 
-class OfflineGetDriveConnectionState : public UIThreadExtensionFunction {
- public:
-  OfflineGetDriveConnectionState() = default;
-
-  ResponseAction Run() override {
-    extensions::api::file_manager_private::DriveConnectionState result;
-    result.type = "offline";
-    return RespondNow(
-        ArgumentList(extensions::api::file_manager_private::
-                         GetDriveConnectionState::Results::Create(result)));
-  }
-
- private:
-  ~OfflineGetDriveConnectionState() override = default;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflineGetDriveConnectionState);
-};
-
 base::Lock& GetLockForBlockingDefaultFileTaskRunner() {
   static base::NoDestructor<base::Lock> lock;
   return *lock;
@@ -1508,6 +1490,10 @@
     command_line->AppendSwitch(switches::kIncognito);
   }
 
+  if (IsOfflineTest()) {
+    command_line->AppendSwitchASCII(chromeos::switches::kShillStub, "clear=1");
+  }
+
   std::vector<base::Feature> enabled_features;
   std::vector<base::Feature> disabled_features;
 
@@ -1666,12 +1652,6 @@
   display_service_ =
       std::make_unique<NotificationDisplayServiceTester>(profile());
 
-  if (IsOfflineTest()) {
-    ExtensionFunctionRegistry::GetInstance().OverrideFunctionForTesting(
-        "fileManagerPrivate.getDriveConnectionState",
-        &NewExtensionFunction<OfflineGetDriveConnectionState>);
-  }
-
   content::NetworkConnectionChangeSimulator network_change_simulator;
   network_change_simulator.SetConnectionType(
       IsOfflineTest() ? network::mojom::ConnectionType::CONNECTION_NONE
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 16db619e..87fb383 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -683,6 +683,8 @@
   SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
   SET_STRING("PASTE_INTO_FOLDER_BUTTON_LABEL",
              IDS_FILE_BROWSER_PASTE_INTO_FOLDER_BUTTON_LABEL);
+  SET_STRING("PLUGIN_VM_DIRECTORY_LABEL",
+             IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL);
   SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL);
   SET_STRING("QUICK_VIEW_CLOSE_BUTTON_LABEL",
              IDS_FILE_BROWSER_QUICK_VIEW_CLOSE_BUTTON_LABEL);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
index 9476a97..75b68240 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -87,6 +87,10 @@
   RunTest("menu");
 }
 
+IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVm) {
+  RunTest("pluginVm");
+}
+
 IN_PROC_BROWSER_TEST_F(FileManagerUITest, PluginVmShare) {
   RunTest("pluginVmShare");
 }
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
index 2d3513d..7d604b2 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
@@ -128,6 +128,26 @@
       std::string() /* entity_id */, policy.SerializeAsString());
 }
 
+bool LocalPolicyTestServerMixin::UpdateUserPolicy(
+    const base::Value& mandatory_policy,
+    const base::Value& recommended_policy,
+    const std::string& policy_user) {
+  DCHECK(policy_test_server_);
+  base::Value policy_type_dict(base::Value::Type::DICTIONARY);
+  policy_type_dict.SetKey("mandatory", mandatory_policy.Clone());
+  policy_type_dict.SetKey("recommended", recommended_policy.Clone());
+
+  base::Value managed_users_list(base::Value::Type::LIST);
+  managed_users_list.GetList().emplace_back("*");
+
+  server_config_.SetKey(policy::dm_protocol::kChromeUserPolicyType,
+                        std::move(policy_type_dict));
+  server_config_.SetKey("managed_users", std::move(managed_users_list));
+  server_config_.SetKey("policy_user", base::Value(policy_user));
+  server_config_.SetKey("current_key_index", base::Value(0));
+  return policy_test_server_->SetConfig(server_config_);
+}
+
 void LocalPolicyTestServerMixin::SetFakeAttestationFlow() {
   g_browser_process->platform_part()
       ->browser_policy_connector_chromeos()
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
index f21ca4a..d9b3f5c 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOCAL_POLICY_TEST_SERVER_MIXIN_H_
 
 #include <memory>
+#include <string>
 
 #include "base/macros.h"
 #include "base/values.h"
@@ -34,6 +35,10 @@
   bool UpdateDevicePolicy(
       const enterprise_management::ChromeDeviceSettingsProto& policy);
 
+  bool UpdateUserPolicy(const base::Value& mandatory_policy,
+                        const base::Value& recommended_policy,
+                        const std::string& policy_user);
+
   // Configures and sets expectations for enrollment flow with license
   // selection. Non-negative values indicate number of available licenses.
   // There should be at least one license type.
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.cc b/chrome/browser/chromeos/policy/login_policy_test_base.cc
index afdb76f..cdf624e 100644
--- a/chrome/browser/chromeos/policy/login_policy_test_base.cc
+++ b/chrome/browser/chromeos/policy/login_policy_test_base.cc
@@ -43,19 +43,15 @@
 
 LoginPolicyTestBase::~LoginPolicyTestBase() = default;
 
-void LoginPolicyTestBase::SetUp() {
+void LoginPolicyTestBase::SetUpInProcessBrowserTestFixture() {
+  OobeBaseTest::SetUpInProcessBrowserTestFixture();
   base::DictionaryValue mandatory;
   GetMandatoryPoliciesValue(&mandatory);
   base::DictionaryValue recommended;
   GetRecommendedPoliciesValue(&recommended);
-  user_policy_helper_.reset(new UserPolicyTestHelper(GetAccount()));
-  user_policy_helper_->Init(mandatory, recommended);
-  OobeBaseTest::SetUp();
-}
-
-void LoginPolicyTestBase::SetUpCommandLine(base::CommandLine* command_line) {
-  user_policy_helper_->UpdateCommandLine(command_line);
-  OobeBaseTest::SetUpCommandLine(command_line);
+  user_policy_helper_.reset(
+      new UserPolicyTestHelper(GetAccount(), &local_policy_server_));
+  user_policy_helper_->SetPolicy(mandatory, recommended);
 }
 
 void LoginPolicyTestBase::SetUpOnMainThread() {
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.h b/chrome/browser/chromeos/policy/login_policy_test_base.h
index a6ac354..10316b6 100644
--- a/chrome/browser/chromeos/policy/login_policy_test_base.h
+++ b/chrome/browser/chromeos/policy/login_policy_test_base.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
+#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 
 namespace base {
@@ -28,8 +29,7 @@
   ~LoginPolicyTestBase() override;
 
   // chromeos::OobeBaseTest::
-  void SetUp() override;
-  void SetUpCommandLine(base::CommandLine* command_line) override;
+  void SetUpInProcessBrowserTestFixture() override;
   void SetUpOnMainThread() override;
 
   virtual void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const;
@@ -52,6 +52,7 @@
   static const char kEmptyServices[];
 
   chromeos::FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
+  chromeos::LocalPolicyTestServerMixin local_policy_server_{&mixin_host_};
 
  private:
   void SetUpGaiaServerWithAccessTokens();
diff --git a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
index bd1e69ff..f9f7c1b3 100644
--- a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
@@ -86,7 +86,8 @@
   std::unique_ptr<base::DictionaryValue> policy =
       std::make_unique<base::DictionaryValue>();
   policy->SetKey(key::kWallpaperImage, base::Value(value));
-  user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile);
+  user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(),
+                                         profile);
 
   UserCloudPolicyManagerChromeOS* policy_manager =
       UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
index e837e9ad..0fafca4 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
@@ -154,8 +154,8 @@
 }
 
 IN_PROC_BROWSER_TEST_P(UserCloudPolicyManagerTest, ErrorLoadingPolicy) {
-  // Delete the policy file - this will cause a 500 error on policy requests.
-  user_policy_helper()->DeletePolicyFile();
+  local_policy_server_.SetExpectedPolicyFetchError(500);
+
   SkipToLoginScreen();
   CountNotificationObserver observer(
       chrome::NOTIFICATION_SESSION_STARTED,
@@ -185,8 +185,7 @@
       account_id,
       user_manager::known_user::ProfileRequiresPolicy::kNoPolicyRequired);
 
-  // Delete the policy file - this will cause a 500 error on policy requests.
-  user_policy_helper()->DeletePolicyFile();
+  local_policy_server_.SetExpectedPolicyFetchError(500);
   SkipToLoginScreen();
   LogIn(kAccountId, kAccountPassword, kEmptyServices);
 
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.cc b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
index a6e5e3d..82b9c666 100644
--- a/chrome/browser/chromeos/policy/user_policy_test_helper.cc
+++ b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
@@ -7,18 +7,15 @@
 #include <utility>
 
 #include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_writer.h"
 #include "base/run_loop.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
-#include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
@@ -32,58 +29,18 @@
 
 namespace policy {
 
-namespace {
-
-std::string BuildPolicy(const base::DictionaryValue& mandatory,
-                        const base::DictionaryValue& recommended,
-                        const std::string& policyType,
-                        const std::string& account_id) {
-  std::unique_ptr<base::DictionaryValue> policy_type_dict(
-      new base::DictionaryValue);
-  policy_type_dict->SetWithoutPathExpansion("mandatory",
-                                            mandatory.CreateDeepCopy());
-  policy_type_dict->SetWithoutPathExpansion("recommended",
-                                            recommended.CreateDeepCopy());
-
-  std::unique_ptr<base::ListValue> managed_users_list(new base::ListValue);
-  managed_users_list->AppendString("*");
-
-  base::DictionaryValue root_dict;
-  root_dict.SetWithoutPathExpansion(policyType, std::move(policy_type_dict));
-  root_dict.SetWithoutPathExpansion("managed_users",
-                                    std::move(managed_users_list));
-  root_dict.SetKey("policy_user", base::Value(account_id));
-  root_dict.SetKey("current_key_index", base::Value(0));
-
-  std::string json_policy;
-  base::JSONWriter::WriteWithOptions(
-      root_dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_policy);
-  return json_policy;
-}
-
-}  // namespace
-
-UserPolicyTestHelper::UserPolicyTestHelper(const std::string& account_id)
-    : account_id_(account_id) {
-}
+UserPolicyTestHelper::UserPolicyTestHelper(
+    const std::string& account_id,
+    chromeos::LocalPolicyTestServerMixin* local_policy_server)
+    : account_id_(account_id), local_policy_server_(local_policy_server) {}
 
 UserPolicyTestHelper::~UserPolicyTestHelper() {
 }
 
-void UserPolicyTestHelper::Init(
-    const base::DictionaryValue& mandatory_policy,
-    const base::DictionaryValue& recommended_policy) {
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  WritePolicyFile(mandatory_policy, recommended_policy);
-
-  test_server_.reset(new LocalPolicyTestServer(PolicyFilePath()));
-  ASSERT_TRUE(test_server_->Start());
-}
-
-void UserPolicyTestHelper::UpdateCommandLine(
-    base::CommandLine* command_line) const {
-  command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
-                                  test_server_->GetServiceURL().spec());
+void UserPolicyTestHelper::SetPolicy(const base::DictionaryValue& mandatory,
+                                     const base::DictionaryValue& recommended) {
+  ASSERT_TRUE(local_policy_server_->UpdateUserPolicy(mandatory, recommended,
+                                                     account_id_));
 }
 
 void UserPolicyTestHelper::WaitForInitialPolicy(Profile* profile) {
@@ -119,11 +76,11 @@
   run_loop.Run();
 }
 
-void UserPolicyTestHelper::UpdatePolicy(
+void UserPolicyTestHelper::SetPolicyAndWait(
     const base::DictionaryValue& mandatory_policy,
     const base::DictionaryValue& recommended_policy,
     Profile* profile) {
-  WritePolicyFile(mandatory_policy, recommended_policy);
+  SetPolicy(mandatory_policy, recommended_policy);
 
   policy::ProfilePolicyConnector* const profile_connector =
       policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
@@ -135,25 +92,4 @@
   run_loop.Run();
 }
 
-void UserPolicyTestHelper::DeletePolicyFile() {
-  base::ScopedAllowBlockingForTesting allow_io;
-  base::DeleteFile(PolicyFilePath(), false);
-}
-
-void UserPolicyTestHelper::WritePolicyFile(
-    const base::DictionaryValue& mandatory,
-    const base::DictionaryValue& recommended) {
-  const std::string policy = BuildPolicy(
-      mandatory, recommended, dm_protocol::kChromeUserPolicyType, account_id_);
-
-  base::ScopedAllowBlockingForTesting allow_io;
-  const int bytes_written =
-      base::WriteFile(PolicyFilePath(), policy.data(), policy.size());
-  ASSERT_EQ(static_cast<int>(policy.size()), bytes_written);
-}
-
-base::FilePath UserPolicyTestHelper::PolicyFilePath() const {
-  return temp_dir_.GetPath().AppendASCII("policy.json");
-}
-
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.h b/chrome/browser/chromeos/policy/user_policy_test_helper.h
index 5026456c..c3304895 100644
--- a/chrome/browser/chromeos/policy/user_policy_test_helper.h
+++ b/chrome/browser/chromeos/policy/user_policy_test_helper.h
@@ -14,29 +14,26 @@
 class Profile;
 
 namespace base {
-class CommandLine;
-class FilePath;
 class DictionaryValue;
 }
 
-namespace policy {
+namespace chromeos {
+class LocalPolicyTestServerMixin;
+}
 
-class LocalPolicyTestServer;
+namespace policy {
 
 // This class can be used to apply a user policy to the profile in a
 // BrowserTest.
 class UserPolicyTestHelper {
  public:
-  explicit UserPolicyTestHelper(const std::string& account_id);
+  UserPolicyTestHelper(
+      const std::string& account_id,
+      chromeos::LocalPolicyTestServerMixin* local_policy_server);
   virtual ~UserPolicyTestHelper();
 
-  // Must be called after construction to start the policy test server.
-  void Init(const base::DictionaryValue& mandatory_policy,
-            const base::DictionaryValue& recommended_policy);
-
-  // Must be used during BrowserTestBase::SetUpCommandLine to direct Chrome to
-  // the policy test server.
-  void UpdateCommandLine(base::CommandLine* command_line) const;
+  void SetPolicy(const base::DictionaryValue& mandatory,
+                 const base::DictionaryValue& recommended);
 
   // Can be optionally used to wait for the initial policy to be applied to the
   // profile. Alternatively, a login can be simulated, which makes it
@@ -45,20 +42,13 @@
 
   // Update the policy test server with the given policy. Then refresh and wait
   // for the new policy being applied to |profile|.
-  void UpdatePolicy(const base::DictionaryValue& mandatory_policy,
-                    const base::DictionaryValue& recommended_policy,
-                    Profile* profile);
-
-  void DeletePolicyFile();
+  void SetPolicyAndWait(const base::DictionaryValue& mandatory_policy,
+                        const base::DictionaryValue& recommended_policy,
+                        Profile* profile);
 
  private:
-  void WritePolicyFile(const base::DictionaryValue& mandatory,
-                       const base::DictionaryValue& recommended);
-  base::FilePath PolicyFilePath() const;
-
   const std::string account_id_;
-  base::ScopedTempDir temp_dir_;
-  std::unique_ptr<LocalPolicyTestServer> test_server_;
+  chromeos::LocalPolicyTestServerMixin* local_policy_server_;
 
   DISALLOW_COPY_AND_ASSIGN(UserPolicyTestHelper);
 };
diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc
index a7387fed..ac22f7e2 100644
--- a/chrome/browser/download/offline_item_model.cc
+++ b/chrome/browser/download/offline_item_model.cc
@@ -144,7 +144,7 @@
       return download::DownloadItem::COMPLETE;
     case OfflineItemState::CANCELLED:
       return download::DownloadItem::CANCELLED;
-    case OfflineItemState::MAX_DOWNLOAD_STATE:
+    case OfflineItemState::NUM_ENTRIES:
       NOTREACHED();
       return download::DownloadItem::CANCELLED;
   }
@@ -181,7 +181,7 @@
       FALLTHROUGH;
     case OfflineItemState::CANCELLED:
       return true;
-    case OfflineItemState::MAX_DOWNLOAD_STATE:
+    case OfflineItemState::NUM_ENTRIES:
       NOTREACHED();
   }
   return false;
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index 574534c1..c00c75c3 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -29,11 +29,11 @@
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
 #include "chrome/common/extensions/extension_metrics.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
@@ -368,7 +368,10 @@
 bool ChromeManagementAPIDelegate::IsWebAppInstalled(
     content::BrowserContext* context,
     const GURL& web_app_url) const {
-  return extensions::BookmarkOrHostedAppInstalled(context, web_app_url);
+  auto* provider = web_app::WebAppProviderBase::GetProviderBase(
+      Profile::FromBrowserContext(context));
+  DCHECK(provider);
+  return provider->registrar().IsInstalled(web_app_url);
 }
 
 bool ChromeManagementAPIDelegate::CanContextInstallWebApps(
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 1616369c7..c4e8062 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -16,7 +16,8 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/browser/api/management/management_api.h"
@@ -258,12 +259,13 @@
 
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
   const GURL good_web_app_url = https_test_server_.GetURL(kGoodWebAppURL);
-  EXPECT_FALSE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(),
-                                                        good_web_app_url));
+
+  auto* provider =
+      web_app::WebAppProviderBase::GetProviderBase(browser()->profile());
+  EXPECT_FALSE(provider->registrar().IsInstalled(good_web_app_url));
 
   RunTest(kGoodWebAppURL, kBackground, true /* from_webstore */);
-  EXPECT_TRUE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(),
-                                                       good_web_app_url));
+  EXPECT_TRUE(provider->registrar().IsInstalled(good_web_app_url));
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index acac44fc..044a42c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -600,8 +600,8 @@
   },
   {
     "name": "discover-app",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "alemate" ],
+    "expiry_milestone": 80
   },
   {
     "name": "document-passive-event-listeners",
@@ -792,6 +792,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-autofill-do-not-migrate-unsupported-local-cards",
+    "owners": [ "sujiezhu@google.com", "jsaul@google.com" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "enable-autofill-do-not-upload-save-unsupported-cards",
     "owners": [ "annelim@google.com", "jsaul@google.com" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 52029d1..11ddf2f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -430,6 +430,12 @@
     "offering card upload to Google Payments, the offer-to-save dialog "
     "displays an expiration date selector.";
 
+const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsName[] =
+    "Prevents local card migration on local cards from unsupported networks";
+const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription[] =
+    "If enabled, local cards from unsupported networks will not be offered "
+    "local card migration.";
+
 const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[] =
     "Prevents upload save on cards from unsupported networks";
 const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 4903a2d7..0e13d9b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -285,6 +285,9 @@
 extern const char
     kEnableAutofillCreditCardUploadEditableExpirationDateDescription[];
 
+extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsName[];
+extern const char kEnableAutofillDoNotMigrateUnsupportedLocalCardsDescription[];
+
 extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[];
 extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[];
 
diff --git a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
index 1d8a518a..f1ff805f8 100644
--- a/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
+++ b/chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_browsertest.cc
@@ -200,11 +200,6 @@
   void SetUpCommandLine(base::CommandLine* cl) override {
     ChromeMimeHandlerViewTestBase::SetUpCommandLine(cl);
     is_cross_process_mode_ = GetParam();
-    // TODO(ekaramad): All these tests started timing out on ChromeOS (https://
-    // crbug.com/949565).
-#if defined(OS_CHROMEOS)
-    is_cross_process_mode_ = false;
-#endif
     if (is_cross_process_mode_) {
       scoped_feature_list_.InitAndEnableFeature(
           features::kMimeHandlerViewInCrossProcessFrame);
@@ -560,6 +555,12 @@
 
 IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
                        UMA_SameOriginResource) {
+#if defined(OS_CHROMEOS)
+  // TODO(ekaramad): This test started timing out on ChromeOS (https://
+  // crbug.com/949565).
+  if (GetParam())
+    return;
+#endif
   auto url = embedded_test_server()->GetURL("a.com", "/testPostMessageUMA.csv");
   auto page_url = embedded_test_server()->GetURL(
       "a.com",
@@ -584,6 +585,12 @@
 
 IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
                        UMA_CrossOriginResource) {
+#if defined(OS_CHROMEOS)
+  // TODO(ekaramad): This test started timing out on ChromeOS (https://
+  // crbug.com/949565).
+  if (GetParam())
+    return;
+#endif
   auto url = embedded_test_server()->GetURL("b.com", "/testPostMessageUMA.csv");
   auto page_url = embedded_test_server()->GetURL(
       "a.com",
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 432e9cf4..60c5f63 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1279,7 +1279,8 @@
   base::ListValue allowed_languages;
   allowed_languages.AppendString("fr");
   policy->SetKey(key::kAllowedLanguages, std::move(allowed_languages));
-  user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile);
+  user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(),
+                                         profile);
 }
 
 IN_PROC_BROWSER_TEST_F(LoginPolicyTestBase, AllowedLanguages) {
@@ -1351,7 +1352,8 @@
   allowed_input_methods.AppendString("xkb:de::ger");
   allowed_input_methods.AppendString("invalid_value_will_be_ignored");
   policy->SetKey(key::kAllowedInputMethods, std::move(allowed_input_methods));
-  user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(), profile);
+  user_policy_helper()->SetPolicyAndWait(*policy, base::DictionaryValue(),
+                                         profile);
 
   // Only "xkb:fr::fra", "xkb:de::ger" should be allowed, current input method
   // should be "xkb:fr::fra", enabling "xkb:us::eng" should not be possible,
@@ -1369,8 +1371,8 @@
   invalid_input_methods.AppendString("invalid_value_will_be_ignored");
   policy_invalid->SetKey(key::kAllowedInputMethods,
                          std::move(invalid_input_methods));
-  user_policy_helper()->UpdatePolicy(*policy_invalid, base::DictionaryValue(),
-                                     profile);
+  user_policy_helper()->SetPolicyAndWait(*policy_invalid,
+                                         base::DictionaryValue(), profile);
 
   // No restrictions and current input method should still be "xkb:fr::fra".
   EXPECT_EQ(0U, ime_state->GetAllowedInputMethods().size());
@@ -1379,8 +1381,8 @@
   EXPECT_TRUE(ime_state->EnableInputMethod(input_methods[2]));
 
   // Allow all input methods again.
-  user_policy_helper()->UpdatePolicy(base::DictionaryValue(),
-                                     base::DictionaryValue(), profile);
+  user_policy_helper()->SetPolicyAndWait(base::DictionaryValue(),
+                                         base::DictionaryValue(), profile);
 
   // No restrictions and current input method should still be "xkb:fr::fra".
   EXPECT_EQ(0U, ime_state->GetAllowedInputMethods().size());
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
index 1453aa6..fb0b66c 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
@@ -7,16 +7,17 @@
 #include <set>
 
 #include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
 
 namespace app_list {
 
@@ -65,6 +66,7 @@
   dependent_factories.insert(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
   dependent_factories.insert(ArcAppListPrefsFactory::GetInstance());
+  dependent_factories.insert(apps::AppServiceProxyFactory::GetInstance());
   for (FactorySet::iterator it = dependent_factories.begin();
        it != dependent_factories.end();
        ++it) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index aa2040c..9d152886 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -565,8 +565,7 @@
   return ash::SHELF_ACTION_WINDOW_ACTIVATED;
 }
 
-void ChromeLauncherController::ActiveUserChanged(
-    const std::string& user_email) {
+void ChromeLauncherController::ActiveUserChanged(const AccountId& account_id) {
   // Store the order of running applications for the user which gets inactive.
   RememberUnpinnedRunningApplicationOrder();
   // Coming here the default profile is already switched. All profile specific
@@ -576,17 +575,21 @@
   // set it as active.
   AttachProfile(ProfileManager::GetActiveUserProfile());
   // Update the V1 applications.
-  browser_status_monitor_->ActiveUserChanged(user_email);
+  browser_status_monitor_->ActiveUserChanged(account_id.GetUserEmail());
+  // Save/restore spinners belonging to the old/new user. Must be called before
+  // notifying the AppWindowControllers, as some of them assume spinners owned
+  // by the new user have already been added to the shelf.
+  shelf_spinner_controller_->ActiveUserChanged(account_id);
   // Switch the running applications to the new user.
   for (auto& controller : app_window_controllers_)
-    controller->ActiveUserChanged(user_email);
+    controller->ActiveUserChanged(account_id.GetUserEmail());
   // Update the user specific shell properties from the new user profile.
   // Shelf preferences are loaded in ChromeLauncherController::AttachProfile.
   UpdateAppLaunchersFromSync();
 
   // Restore the order of running, but unpinned applications for the activated
   // user.
-  RestoreUnpinnedRunningApplicationOrder(user_email);
+  RestoreUnpinnedRunningApplicationOrder(account_id.GetUserEmail());
 }
 
 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index fe579d24f..f47f8f81 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/ash/launcher/discover_window_observer.h"
 #include "chrome/browser/ui/ash/launcher/launcher_app_updater.h"
 #include "chrome/browser/ui/ash/launcher/settings_window_observer.h"
+#include "components/account_id/account_id.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/sync_preferences/pref_service_syncable_observer.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
@@ -169,7 +170,7 @@
                                                     bool allow_minimize);
 
   // Called when the active user has changed.
-  void ActiveUserChanged(const std::string& user_email);
+  void ActiveUserChanged(const AccountId& account_id);
 
   // Called when a user got added to the session.
   void AdditionalUserAddedToSession(Profile* profile);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index beef424..873e3cdb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h"
@@ -659,7 +660,10 @@
   }
 
   // Create and initialize the controller, owned by the test shell delegate.
-  void InitLauncherController() { CreateLauncherController()->Init(); }
+  void InitLauncherController() {
+    CreateLauncherController()->Init();
+    FlushBindings();
+  }
 
   // Create and initialize the controller; create a tab and show the browser.
   void InitLauncherControllerWithBrowser() {
@@ -1308,6 +1312,9 @@
     // Call FlushBindings() to ensure ash has completed processing of the
     // switch.
     FlushBindings();
+
+    // TODO(crbug.com/956841) This should be redundant with the FlushBindings
+    // call, but removing it breaks some tests.
     launcher_controller_->browser_status_monitor_for_test()->ActiveUserChanged(
         account_id.GetUserEmail());
 
@@ -3608,6 +3615,108 @@
   }
 }
 
+// Checks that spinners are hidden and restored on profile switching
+TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
+       SpinnersUpdateOnUserSwitch) {
+  InitLauncherController();
+
+  const AccountId account_id(
+      multi_user_util::GetAccountIdFromProfile(profile()));
+  const std::string user2 = "user2";
+  const TestingProfile* profile2 = CreateMultiUserProfile(user2);
+  const AccountId account_id2(
+      multi_user_util::GetAccountIdFromProfile(profile2));
+
+  const std::string app_id = extension1_->id();
+  extension_service_->AddExtension(extension1_.get());
+
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Add a spinner to the shelf
+  launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf(
+      app_id, std::make_unique<ShelfSpinnerItemController>(app_id));
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_TRUE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Switch to a new profile
+  SwitchActiveUser(account_id2);
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Switch back
+  SwitchActiveUser(account_id);
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_TRUE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Close the spinner
+  launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id);
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+}
+
+// Checks that pinned spinners are hidden and restored on profile switching
+// but are not removed when the spinner closes.
+TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
+       PinnedSpinnersUpdateOnUserSwitch) {
+  InitLauncherController();
+
+  const AccountId account_id(
+      multi_user_util::GetAccountIdFromProfile(profile()));
+  const std::string user2 = "user2";
+  const TestingProfile* profile2 = CreateMultiUserProfile(user2);
+  const AccountId account_id2(
+      multi_user_util::GetAccountIdFromProfile(profile2));
+
+  const std::string app_id = extension1_->id();
+  extension_service_->AddExtension(extension1_.get());
+
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Pin an app to the shelf
+  launcher_controller_->PinAppWithID(app_id);
+  EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Activate the spinner
+  launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf(
+      app_id, std::make_unique<ShelfSpinnerItemController>(app_id));
+  EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_TRUE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Switch to a new profile
+  SwitchActiveUser(account_id2);
+  EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Switch back
+  SwitchActiveUser(account_id);
+  EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_TRUE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+
+  // Close the spinner
+  launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id);
+  EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
+  EXPECT_EQ(4, model_->item_count());
+  EXPECT_FALSE(
+      launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
+}
+
 // Checks that the generated menu list properly activates items.
 TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) {
   InitLauncherControllerWithBrowser();
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
index 6fc603e1..a9b74e3 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/app_window_base.h"
 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
@@ -205,9 +206,16 @@
     return;
   }
 
+  // Currently Crostini can only be used from the primary profile. In the
+  // future, this may be replaced by some way of matching the container that
+  // runs this app with the user that owns it.
+  const AccountId& primary_account_id =
+      user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
+
   crostini::CrostiniRegistryService* registry_service =
       crostini::CrostiniRegistryServiceFactory::GetForProfile(
-          owner()->profile());
+          chromeos::ProfileHelper::Get()->GetProfileByAccountId(
+              primary_account_id));
   const std::string& shelf_app_id = registry_service->GetCrostiniShelfAppId(
       exo::GetShellApplicationId(window), exo::GetShellStartupId(window));
   // Windows without an application id set will get filtered out here.
@@ -233,8 +241,7 @@
 
   // Prevent Crostini window from showing up after user switch.
   MultiUserWindowManagerClient::GetInstance()->SetWindowOwner(
-      window,
-      user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId());
+      window, primary_account_id);
 
   RegisterAppWindow(window, shelf_app_id);
 
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
index defee205..5aaf0a7 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
+++ b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
@@ -4,15 +4,17 @@
 
 #include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
 
-#include <memory>
+#include <vector>
 
 #include "ash/public/cpp/shelf_model.h"
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -61,6 +63,14 @@
 ShelfSpinnerController::ShelfSpinnerController(ChromeLauncherController* owner)
     : owner_(owner), weak_ptr_factory_(this) {
   owner->shelf_model()->AddObserver(this);
+  if (user_manager::UserManager::IsInitialized()) {
+    if (auto* active_user = user_manager::UserManager::Get()->GetActiveUser())
+      current_account_id_ = active_user->GetAccountId();
+    else
+      LOG(ERROR) << "Failed to get active user, UserManager returned null";
+  } else {
+    LOG(ERROR) << "Failed to get active user, UserManager is not initialized";
+  }
 }
 
 ShelfSpinnerController::~ShelfSpinnerController() {
@@ -83,31 +93,61 @@
       image->size());
 }
 
-void ShelfSpinnerController::Close(const std::string& app_id) {
-  // Code below may invalidate passed |app_id|. Use local variable for safety.
-  const std::string safe_app_id(app_id);
-
-  AppControllerMap::const_iterator it = app_controller_map_.find(safe_app_id);
-  if (it == app_controller_map_.end())
+void ShelfSpinnerController::HideSpinner(const std::string& app_id) {
+  if (!RemoveSpinnerFromControllerMap(app_id))
     return;
 
-  const ash::ShelfID shelf_id(safe_app_id);
+  const ash::ShelfID shelf_id(app_id);
+
+  // If the app whose spinner is being hidden is pinned, we don't want to un-pin
+  // it when we remove it from the shelf, so disable pin syncing while we update
+  // things.
+  auto pin_disabler = owner_->GetScopedPinSyncDisabler();
+  // The static_cast here is safe, because if the delegate were not a
+  // ShelfSpinnerItemController then ShelfItemDelegateChanged would have been
+  // called and we would not have reached this place.
+  auto delegate =
+      owner_->shelf_model()->RemoveItemAndTakeShelfItemDelegate(shelf_id);
+  std::unique_ptr<ShelfSpinnerItemController> cast_delegate(
+      static_cast<ShelfSpinnerItemController*>(delegate.release()));
+
+  hidden_app_controller_map_.emplace(
+      current_account_id_, std::make_pair(app_id, std::move(cast_delegate)));
+}
+
+void ShelfSpinnerController::CloseSpinner(const std::string& app_id) {
+  if (!RemoveSpinnerFromControllerMap(app_id))
+    return;
+
+  owner_->CloseLauncherItem(ash::ShelfID(app_id));
+  UpdateShelfItemIcon(app_id);
+}
+
+bool ShelfSpinnerController::RemoveSpinnerFromControllerMap(
+    const std::string& app_id) {
+  AppControllerMap::const_iterator it = app_controller_map_.find(app_id);
+  if (it == app_controller_map_.end())
+    return false;
+
+  const ash::ShelfID shelf_id(app_id);
   DCHECK_EQ(it->second, owner_->shelf_model()->GetShelfItemDelegate(shelf_id));
   app_controller_map_.erase(it);
-  owner_->CloseLauncherItem(shelf_id);
-  UpdateShelfItemIcon(safe_app_id);
+
+  return true;
 }
 
 void ShelfSpinnerController::CloseCrostiniSpinners() {
   std::vector<std::string> app_ids_to_close;
   crostini::CrostiniRegistryService* registry_service =
-      crostini::CrostiniRegistryServiceFactory::GetForProfile(OwnerProfile());
+      crostini::CrostiniRegistryServiceFactory::GetForProfile(
+          chromeos::ProfileHelper::Get()->GetProfileByAccountId(
+              current_account_id_));
   for (const auto& app_id_controller_pair : app_controller_map_) {
     if (registry_service->IsCrostiniShelfAppId(app_id_controller_pair.first))
       app_ids_to_close.push_back(app_id_controller_pair.first);
   }
   for (const auto& app_id : app_ids_to_close)
-    Close(app_id);
+    CloseSpinner(app_id);
 }
 
 bool ShelfSpinnerController::HasApp(const std::string& app_id) const {
@@ -132,12 +172,43 @@
     ash::ShelfItemDelegate* old_delegate,
     ash::ShelfItemDelegate* delegate) {
   auto it = app_controller_map_.find(id.app_id);
-  if (it == app_controller_map_.end())
+  if (it != app_controller_map_.end()) {
+    app_controller_map_.erase(it);
+    UpdateShelfItemIcon(id.app_id);
+  }
+}
+
+void ShelfSpinnerController::ActiveUserChanged(const AccountId& account_id) {
+  if (account_id == current_account_id_) {
+    LOG(WARNING) << "Tried switching to currently active user";
     return;
-  DCHECK_EQ(it->second, old_delegate);
-  app_controller_map_.erase(it);
-  // Update the icon one more time so it doesn't remain stuck with a spinner.
-  UpdateShelfItemIcon(id.app_id);
+  }
+
+  std::vector<std::string> to_hide;
+  std::vector<
+      std::pair<std::string, std::unique_ptr<ShelfSpinnerItemController>>>
+      to_show;
+
+  for (const auto& app_id : app_controller_map_)
+    to_hide.push_back(app_id.first);
+  for (auto it = hidden_app_controller_map_.lower_bound(account_id);
+       it != hidden_app_controller_map_.upper_bound(account_id); it++) {
+    to_show.push_back(std::move(it->second));
+  }
+
+  hidden_app_controller_map_.erase(
+      hidden_app_controller_map_.lower_bound(account_id),
+      hidden_app_controller_map_.upper_bound(account_id));
+
+  for (const auto& app_id : to_hide)
+    HideSpinner(app_id);
+
+  for (auto& app_id_delegate_pair : to_show) {
+    AddSpinnerToShelf(app_id_delegate_pair.first,
+                      std::move(app_id_delegate_pair.second));
+  }
+
+  current_account_id_ = account_id;
 }
 
 void ShelfSpinnerController::UpdateShelfItemIcon(const std::string& app_id) {
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
index bea34f2..c3fa453 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
+++ b/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
@@ -8,13 +8,17 @@
 #include <stdint.h>
 
 #include <map>
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "ash/public/cpp/shelf_model_observer.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "components/account_id/account_id.h"
 
+class ShelfItemDelegate;
 class ShelfSpinnerItemController;
 class ChromeLauncherController;
 class Profile;
@@ -44,9 +48,10 @@
   void MaybeApplySpinningEffect(const std::string& app_id,
                                 gfx::ImageSkia* image);
 
-  // Closes Shelf item if it has ShelfSpinnerItemController controller
-  // and removes entry from the list of tracking items.
-  void Close(const std::string& app_id);
+  // Finishes spinning on an icon. If an icon is pinned it will be kept on the
+  // shelf as a shortcut, otherwise it will be removed without storing the
+  // delegate.
+  void CloseSpinner(const std::string& app_id);
 
   // Closes all Crostini spinner shelf items.
   // This should be avoided when possible.
@@ -54,25 +59,48 @@
 
   Profile* OwnerProfile();
 
+  // Hide all the spinners associated with the old user, and restore to the
+  // shelf any spinners associated with the new active user. Called by
+  // ChromeLauncherController when the active user is changed.
+  void ActiveUserChanged(const AccountId& account_id);
+
   // ash::ShelfModelObserver:
   void ShelfItemDelegateChanged(const ash::ShelfID& id,
                                 ash::ShelfItemDelegate* old_delegate,
                                 ash::ShelfItemDelegate* delegate) override;
 
  private:
-  // Defines mapping of a shelf app id to a corresponded controller. Shelf app
+  // Defines mapping of a shelf app id to a corresponding controller. Shelf app
   // id is optional mapping (for example, Play Store to ARC Host Support).
   using AppControllerMap = std::map<std::string, ShelfSpinnerItemController*>;
+  // Defines a mapping from account id to (app id, ShelfSpinnerItemController)
+  // for spinners that are not currently on the shelf. Taking ownership of these
+  // delegates allows us to reuse them if we need to add the spinner back on to
+  // the shelf.
+  using HiddenAppControllerMap = std::multimap<
+      AccountId,
+      std::pair<std::string, std::unique_ptr<ShelfSpinnerItemController>>>;
 
   void UpdateApps();
   void UpdateShelfItemIcon(const std::string& app_id);
   void RegisterNextUpdate();
+  // Removes the spinner with id |app_id| from |app_controller_map_| and returns
+  // true if it was present, false otherwise.
+  bool RemoveSpinnerFromControllerMap(const std::string& app_id);
+
+  // Removes a spinner from the shelf and stores the delegate for later
+  // restoration. Used when the user switches from one profile to another.
+  void HideSpinner(const std::string& app_id);
 
   // Unowned pointers.
   ChromeLauncherController* owner_;
 
+  AccountId current_account_id_;
+
   AppControllerMap app_controller_map_;
 
+  HiddenAppControllerMap hidden_app_controller_map_;
+
   // Always keep this the last member of this class.
   base::WeakPtrFactory<ShelfSpinnerController> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc b/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
index ea6bb9e3..d044702 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
@@ -21,7 +21,7 @@
 
 void ShelfSpinnerItemController::SetHost(
     const base::WeakPtr<ShelfSpinnerController>& host) {
-  DCHECK(!host_);
+  DCHECK(!host_ || host_.get() == host.get());
   host_ = host;
 }
 
@@ -55,6 +55,10 @@
 }
 
 void ShelfSpinnerItemController::Close() {
-  if (host_)
-    host_->Close(app_id());
+  if (host_) {
+    // CloseSpinner can result in |app_id| being deleted, so make a copy of it
+    // first.
+    const std::string safe_app_id = app_id();
+    host_->CloseSpinner(safe_app_id);
+  }
 }
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util.cc b/chrome/browser/ui/ash/multi_user/multi_user_util.cc
index 6787c13..7b439b4 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_util.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_util.cc
@@ -12,7 +12,7 @@
 
 namespace multi_user_util {
 
-AccountId GetAccountIdFromProfile(Profile* profile) {
+AccountId GetAccountIdFromProfile(const Profile* profile) {
   // This will guarantee an nonempty AccountId be returned if a valid profile is
   // provided.
   const user_manager::User* user =
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util.h b/chrome/browser/ui/ash/multi_user/multi_user_util.h
index 476e68f..26b5a93 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_util.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_util.h
@@ -17,7 +17,7 @@
 namespace multi_user_util {
 
 // Get the user id from a given profile.
-AccountId GetAccountIdFromProfile(Profile* profile);
+AccountId GetAccountIdFromProfile(const Profile* profile);
 
 // Get the user id from an email address.
 AccountId GetAccountIdFromEmail(const std::string& email);
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
index cf57767c..f9d4e6d 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
@@ -428,10 +428,8 @@
   ChromeLauncherController* chrome_launcher_controller =
       ChromeLauncherController::instance();
   // Some unit tests have no ChromeLauncherController.
-  if (chrome_launcher_controller) {
-    chrome_launcher_controller->ActiveUserChanged(
-        current_account_id_.GetUserEmail());
-  }
+  if (chrome_launcher_controller)
+    chrome_launcher_controller->ActiveUserChanged(current_account_id_);
 }
 
 void MultiUserWindowManagerClientImpl::OnDidSwitchActiveAccount() {
diff --git a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
index b52eef1..f5c9f57 100644
--- a/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_layout_model.cc
@@ -71,16 +71,17 @@
     {autofill::kMirCard, IDR_AUTOFILL_CC_MIR, IDS_AUTOFILL_CC_MIR},
     {autofill::kUnionPay, IDR_AUTOFILL_CC_UNIONPAY, IDS_AUTOFILL_CC_UNION_PAY},
     {autofill::kVisaCard, IDR_AUTOFILL_CC_VISA, IDS_AUTOFILL_CC_VISA},
-#if defined(GOOGLE_CHROME_BUILD)
-    {"googlePay", IDR_ANDROID_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId},
-#endif  // GOOGLE_CHROME_BUILD
 #if defined(OS_ANDROID)
     {"httpWarning", IDR_AUTOFILL_HTTP_WARNING, kResourceNotFoundId},
     {"httpsInvalid", IDR_AUTOFILL_HTTPS_INVALID_WARNING, kResourceNotFoundId},
     {"scanCreditCardIcon", IDR_AUTOFILL_CC_SCAN_NEW, kResourceNotFoundId},
     {"settings", IDR_AUTOFILL_SETTINGS, kResourceNotFoundId},
     {"create", IDR_AUTOFILL_CREATE, kResourceNotFoundId},
+#if defined(GOOGLE_CHROME_BUILD)
+    {"googlePay", IDR_ANDROID_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId},
+#endif  // GOOGLE_CHROME_BUILD
 #elif defined(GOOGLE_CHROME_BUILD)
+    {"googlePay", IDR_AUTOFILL_GOOGLE_PAY, kResourceNotFoundId},
     {"googlePayDark", IDR_AUTOFILL_GOOGLE_PAY_DARK, kResourceNotFoundId},
 #endif  // GOOGLE_CHROME_BUILD
 };
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 2a06a54..f4c8d5a 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -795,11 +795,9 @@
   // content.
   RenderFrameHost* subframe = content::ChildFrameAt(tab->GetMainFrame(), 0);
   EXPECT_EQ(app_url, subframe->GetLastCommittedURL());
-  std::string result;
-  EXPECT_TRUE(ExecuteScriptAndExtractString(
-      subframe, "window.domAutomationController.send(document.body.innerText);",
-      &result));
-  EXPECT_EQ("This page has no title.", result);
+  EXPECT_EQ(
+      "This page has no title.",
+      EvalJs(subframe, "document.body.innerText.trim();").ExtractString());
 }
 
 // Check that no assertions are hit when showing a permission request bubble.
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 6bce56ce..5cf5609 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -30,20 +30,17 @@
   if (!web_contents)
     return false;
 
-  banners::AppBannerManager* manager =
-      banners::AppBannerManager::FromWebContents(web_contents);
+  auto* manager = banners::AppBannerManager::FromWebContents(web_contents);
   // May not be present e.g. in incognito mode.
   if (!manager)
     return false;
 
-  bool is_installable = manager->IsProbablyInstallable();
-  bool is_installed =
-      web_app::WebAppTabHelperBase::FromWebContents(web_contents)
-          ->HasAssociatedApp();
-  bool show_install_button = is_installable && !is_installed;
-  // TODO(crbug.com/907351): When installability is unknown and we're still in
-  // the scope of a previously-determined installable site, display it as still
-  // being installable.
+  bool is_probably_installable = manager->IsProbablyInstallable();
+  auto* tab_helper =
+      web_app::WebAppTabHelperBase::FromWebContents(web_contents);
+  bool is_installed = tab_helper && tab_helper->HasAssociatedApp();
+
+  bool show_install_button = is_probably_installable && !is_installed;
 
   if (show_install_button && manager->MaybeConsumeInstallAnimation())
     AnimateIn(base::nullopt);
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
index 5c953070..bc89f9d 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
@@ -10,13 +10,13 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/scoped_observer.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/extensions/bookmark_app_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/install_manager_observer.h"
 #include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -26,7 +26,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/common/extension.h"
 
 namespace extensions {
 
@@ -252,8 +251,10 @@
 
 }  // namespace
 
-BookmarkAppInstallManager::BookmarkAppInstallManager(Profile* profile)
-    : InstallManager(profile) {
+BookmarkAppInstallManager::BookmarkAppInstallManager(
+    Profile* profile,
+    web_app::InstallFinalizer* finalizer)
+    : InstallManager(profile), finalizer_(finalizer) {
   bookmark_app_helper_factory_ = base::BindRepeating(
       [](Profile* profile, const WebApplicationInfo& web_app_info,
          content::WebContents* web_contents,
@@ -380,23 +381,14 @@
       ExtensionSystem::Get(profile())->extension_service();
   DCHECK(extension_service);
 
-  const Extension* extension = extension_service->GetInstalledExtension(app_id);
-
-  // Return if there are no bookmark app details that need updating.
-  const std::string extension_sync_data_name =
-      base::UTF16ToUTF8(web_application_info->title);
-  const std::string bookmark_app_description =
-      base::UTF16ToUTF8(web_application_info->description);
-  if (extension &&
-      extension->non_localized_name() == extension_sync_data_name &&
-      extension->description() == bookmark_app_description) {
+  if (finalizer_->CanSkipAppUpdateForSync(app_id, *web_application_info))
     return;
-  }
 
 #if defined(OS_CHROMEOS)
-  const bool is_locally_installed = true;
+  bool is_locally_installed = true;
 #else
-  const bool is_locally_installed = extension != nullptr;
+  bool is_locally_installed =
+      extension_service->GetInstalledExtension(app_id) != nullptr;
 #endif
 
   CreateOrUpdateBookmarkApp(extension_service, web_application_info.get(),
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
index 27ca81f..77e1336 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
@@ -20,6 +20,7 @@
 }
 
 namespace web_app {
+class InstallFinalizer;
 class WebAppDataRetriever;
 }
 
@@ -31,7 +32,8 @@
 // crbug.com/915043.
 class BookmarkAppInstallManager final : public web_app::InstallManager {
  public:
-  explicit BookmarkAppInstallManager(Profile* profile);
+  BookmarkAppInstallManager(Profile* profile,
+                            web_app::InstallFinalizer* finalizer);
   ~BookmarkAppInstallManager() override;
 
   // InstallManager:
@@ -83,6 +85,7 @@
  private:
   BookmarkAppHelperFactory bookmark_app_helper_factory_;
   DataRetrieverFactory data_retriever_factory_;
+  web_app::InstallFinalizer* finalizer_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallManager);
 };
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
index 4c3ace7..b7ce597 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
+#include "chrome/browser/web_applications/test/test_install_finalizer.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -33,7 +34,9 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
 
-    install_manager_ = std::make_unique<BookmarkAppInstallManager>(profile());
+    install_finalizer_ = std::make_unique<web_app::TestInstallFinalizer>();
+    install_manager_ = std::make_unique<BookmarkAppInstallManager>(
+        profile(), install_finalizer_.get());
 
     extensions::TestExtensionSystem* test_extension_system =
         static_cast<extensions::TestExtensionSystem*>(
@@ -69,6 +72,7 @@
   }
 
  protected:
+  std::unique_ptr<web_app::TestInstallFinalizer> install_finalizer_;
   std::unique_ptr<BookmarkAppInstallManager> install_manager_;
 };
 
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index 21a3c27f..4ae6254a 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -8,6 +8,8 @@
 #include "base/callback_forward.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 
+class GURL;
+
 namespace web_app {
 
 class AppRegistrar {
@@ -16,6 +18,12 @@
 
   virtual void Init(base::OnceClosure callback) = 0;
 
+  // Returns true if the app with the specified |start_url| is currently fully
+  // locally installed. The provided |start_url| must exactly match the launch
+  // URL for the app; this method does not consult the app scope or match URLs
+  // that fall within the scope.
+  virtual bool IsInstalled(const GURL& start_url) const = 0;
+
   // Returns true if the app with |app_id| is currently installed.
   virtual bool IsInstalled(const AppId& app_id) const = 0;
 
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h
index 56a8465..a099ebd1 100644
--- a/chrome/browser/web_applications/components/install_finalizer.h
+++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -60,6 +60,10 @@
   virtual bool CanRevealAppShim() const = 0;
   virtual void RevealAppShim(const AppId& app_id) = 0;
 
+  virtual bool CanSkipAppUpdateForSync(
+      const AppId& app_id,
+      const WebApplicationInfo& web_app_info) const = 0;
+
   virtual ~InstallFinalizer() = default;
 };
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index 123be10..f9c7c79 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -9,8 +9,10 @@
 
 #include "base/bind.h"
 #include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/bookmark_app_extension_util.h"
 #include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -19,6 +21,7 @@
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
 #include "extensions/browser/install/crx_install_error.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_id.h"
@@ -165,6 +168,31 @@
   BookmarkAppRevealAppShim(profile_, app);
 }
 
+bool BookmarkAppInstallFinalizer::CanSkipAppUpdateForSync(
+    const web_app::AppId& app_id,
+    const WebApplicationInfo& web_app_info) const {
+  ExtensionService* extension_service =
+      ExtensionSystem::Get(profile_)->extension_service();
+  DCHECK(extension_service);
+
+  const Extension* extension = extension_service->GetInstalledExtension(app_id);
+  if (!extension)
+    return false;
+
+  // We can skip if there are no bookmark app details that need updating.
+  // TODO(loyso): We need to check more data fields. crbug.com/949427.
+  const std::string extension_sync_data_name =
+      base::UTF16ToUTF8(web_app_info.title);
+  const std::string bookmark_app_description =
+      base::UTF16ToUTF8(web_app_info.description);
+  if (extension->non_localized_name() == extension_sync_data_name &&
+      extension->description() == bookmark_app_description) {
+    return true;
+  }
+
+  return false;
+}
+
 void BookmarkAppInstallFinalizer::SetCrxInstallerFactoryForTesting(
     CrxInstallerFactory crx_installer_factory) {
   crx_installer_factory_ = crx_installer_factory;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index 40a934ff..42685c5 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -41,6 +41,9 @@
                    content::WebContents* web_contents) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const web_app::AppId& app_id) override;
+  bool CanSkipAppUpdateForSync(
+      const web_app::AppId& app_id,
+      const WebApplicationInfo& web_app_info) const override;
 
   using CrxInstallerFactory =
       base::RepeatingCallback<scoped_refptr<CrxInstaller>(Profile*)>;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
index af3c03d..e2f64ef8 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -297,4 +297,41 @@
   run_loop.Run();
 }
 
+TEST_F(BookmarkAppInstallFinalizerTest, CanSkipAppUpdateForSync) {
+  BookmarkAppInstallFinalizer installer(profile());
+
+  auto info = std::make_unique<WebApplicationInfo>();
+  info->app_url = GURL(kWebAppUrl);
+  info->title = base::ASCIIToUTF16("Title1");
+  info->description = base::ASCIIToUTF16("Description1");
+
+  const web_app::AppId app_id = web_app::GenerateAppIdFromURL(info->app_url);
+
+  EXPECT_FALSE(installer.CanSkipAppUpdateForSync(app_id, *info));
+
+  base::RunLoop run_loop;
+  web_app::InstallFinalizer::FinalizeOptions options;
+
+  installer.FinalizeInstall(
+      *info, options,
+      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                     web_app::InstallResultCode code) {
+        EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
+        EXPECT_EQ(app_id, installed_app_id);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  EXPECT_TRUE(installer.CanSkipAppUpdateForSync(app_id, *info));
+
+  WebApplicationInfo info_with_diff_title = *info;
+  info_with_diff_title.title = base::ASCIIToUTF16("Title2");
+  EXPECT_FALSE(installer.CanSkipAppUpdateForSync(app_id, info_with_diff_title));
+
+  WebApplicationInfo info_with_diff_description = *info;
+  info_with_diff_description.title = base::ASCIIToUTF16("Description2");
+  EXPECT_FALSE(
+      installer.CanSkipAppUpdateForSync(app_id, info_with_diff_description));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index 33e73ce..90f2588 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -9,10 +9,14 @@
 #include "base/one_shot_event.h"
 #include "chrome/browser/extensions/convert_web_app.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "url/gurl.h"
 
 namespace extensions {
 
@@ -25,6 +29,27 @@
   ExtensionSystem::Get(profile_)->ready().Post(FROM_HERE, std::move(callback));
 }
 
+bool BookmarkAppRegistrar::IsInstalled(const GURL& start_url) const {
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
+  const ExtensionSet& extensions = registry->enabled_extensions();
+
+  // Iterate through the extensions and extract the LaunchWebUrl (bookmark apps)
+  // or check the web extent (hosted apps).
+  for (const scoped_refptr<const Extension>& extension : extensions) {
+    if (!extension->is_hosted_app())
+      continue;
+
+    if (!BookmarkAppIsLocallyInstalled(profile_, extension.get()))
+      continue;
+
+    if (extension->web_extent().MatchesURL(start_url) ||
+        AppLaunchInfo::GetLaunchWebURL(extension.get()) == start_url) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool BookmarkAppRegistrar::IsInstalled(const web_app::AppId& app_id) const {
   return GetExtension(app_id) != nullptr;
 }
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index f31fe1d..5ab43a4 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -21,6 +21,7 @@
 
   // AppRegistrar
   void Init(base::OnceClosure callback) override;
+  bool IsInstalled(const GURL& start_url) const override;
   bool IsInstalled(const web_app::AppId& app_id) const override;
   bool WasExternalAppUninstalledByUser(
       const web_app::AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.cc b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
index ebf56ac..c5985bd 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_util.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
@@ -55,28 +55,6 @@
   return true;
 }
 
-bool BookmarkOrHostedAppInstalled(content::BrowserContext* browser_context,
-                                  const GURL& url) {
-  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context);
-  const ExtensionSet& extensions = registry->enabled_extensions();
-
-  // Iterate through the extensions and extract the LaunchWebUrl (bookmark apps)
-  // or check the web extent (hosted apps).
-  for (const scoped_refptr<const Extension>& extension : extensions) {
-    if (!extension->is_hosted_app())
-      continue;
-
-    if (!BookmarkAppIsLocallyInstalled(browser_context, extension.get()))
-      continue;
-
-    if (extension->web_extent().MatchesURL(url) ||
-        AppLaunchInfo::GetLaunchWebURL(extension.get()) == url) {
-      return true;
-    }
-  }
-  return false;
-}
-
 bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url) {
   // Drop any "suffix" components after the path (Resolve "."):
   const GURL nav_scope = launch_url.GetWithoutFilename();
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.h b/chrome/browser/web_applications/extensions/bookmark_app_util.h
index 6d539db..fb475aea 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_util.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_util.h
@@ -34,11 +34,6 @@
 bool BookmarkAppIsLocallyInstalled(const ExtensionPrefs* prefs,
                                    const Extension* extension);
 
-// Returns true if a bookmark or hosted app from a given URL is already
-// installed and enabled.
-bool BookmarkOrHostedAppInstalled(content::BrowserContext* browser_context,
-                                  const GURL& url);
-
 // Generates a scope based on |launch_url| and checks if the |url| falls under
 // it. https://www.w3.org/TR/appmanifest/#navigation-scope
 bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url);
diff --git a/chrome/browser/web_applications/test/test_app_registrar.cc b/chrome/browser/web_applications/test/test_app_registrar.cc
index bae4cb2b..6e31395 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.cc
+++ b/chrome/browser/web_applications/test/test_app_registrar.cc
@@ -30,6 +30,11 @@
 
 void TestAppRegistrar::Init(base::OnceClosure callback) {}
 
+bool TestAppRegistrar::IsInstalled(const GURL& start_url) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 bool TestAppRegistrar::IsInstalled(const AppId& app_id) const {
   return base::ContainsKey(installed_apps_, app_id);
 }
diff --git a/chrome/browser/web_applications/test/test_app_registrar.h b/chrome/browser/web_applications/test/test_app_registrar.h
index 14bb9bc..0cf1aa3 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.h
+++ b/chrome/browser/web_applications/test/test_app_registrar.h
@@ -28,6 +28,7 @@
 
   // AppRegistrar
   void Init(base::OnceClosure callback) override;
+  bool IsInstalled(const GURL& start_url) const override;
   bool IsInstalled(const AppId& app_id) const override;
   bool WasExternalAppUninstalledByUser(const AppId& app_id) const override;
   bool HasScopeUrl(const AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.cc b/chrome/browser/web_applications/test/test_install_finalizer.cc
index 7f5b5e72..8ac6e3e 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.cc
+++ b/chrome/browser/web_applications/test/test_install_finalizer.cc
@@ -82,6 +82,12 @@
   ++num_reveal_appshim_calls_;
 }
 
+bool TestInstallFinalizer::CanSkipAppUpdateForSync(
+    const AppId& app_id,
+    const WebApplicationInfo& web_app_info) const {
+  return false;
+}
+
 void TestInstallFinalizer::SetNextFinalizeInstallResult(
     const AppId& app_id,
     InstallResultCode code) {
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.h b/chrome/browser/web_applications/test/test_install_finalizer.h
index f073d7e6..7aff46b 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.h
+++ b/chrome/browser/web_applications/test/test_install_finalizer.h
@@ -36,6 +36,9 @@
                    content::WebContents* web_contents) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
+  bool CanSkipAppUpdateForSync(
+      const AppId& app_id,
+      const WebApplicationInfo& web_app_info) const override;
 
   void SetNextFinalizeInstallResult(const AppId& app_id,
                                     InstallResultCode code);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index e4c735c..91da2b0 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -149,4 +149,11 @@
   NOTIMPLEMENTED();
 }
 
+bool WebAppInstallFinalizer::CanSkipAppUpdateForSync(
+    const AppId& app_id,
+    const WebApplicationInfo& web_app_info) const {
+  NOTIMPLEMENTED();
+  return true;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index b9bd908..e1a8bb4 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -41,6 +41,9 @@
                    content::WebContents* web_contents) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
+  bool CanSkipAppUpdateForSync(
+      const AppId& app_id,
+      const WebApplicationInfo& web_app_info) const override;
 
  private:
   void OnDataWritten(InstallFinalizedCallback callback,
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index fd2e267..edec6f4 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -130,8 +130,8 @@
     install_manager_ = std::make_unique<WebAppInstallManager>(
         profile, install_finalizer_.get());
   } else {
-    install_manager_ =
-        std::make_unique<extensions::BookmarkAppInstallManager>(profile);
+    install_manager_ = std::make_unique<extensions::BookmarkAppInstallManager>(
+        profile, install_finalizer_.get());
   }
 
   pending_app_manager_ =
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index 649f1324..01843030 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -74,6 +74,11 @@
   std::move(callback).Run();
 }
 
+bool WebAppRegistrar::IsInstalled(const GURL& start_url) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 bool WebAppRegistrar::IsInstalled(const AppId& app_id) const {
   return GetAppById(app_id) != nullptr;
 }
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index b14c87ac..cb7def4 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -37,6 +37,7 @@
 
   // AppRegistrar
   void Init(base::OnceClosure callback) override;
+  bool IsInstalled(const GURL& start_url) const override;
   bool IsInstalled(const AppId& app_id) const override;
   bool WasExternalAppUninstalledByUser(const AppId& app_id) const override;
   bool HasScopeUrl(const AppId& app_id) const override;
diff --git a/chrome/services/app_service/app_service_impl.h b/chrome/services/app_service/app_service_impl.h
index 61688131..27287ee2 100644
--- a/chrome/services/app_service/app_service_impl.h
+++ b/chrome/services/app_service/app_service_impl.h
@@ -54,12 +54,15 @@
  private:
   void OnPublisherDisconnected(apps::mojom::AppType app_type);
 
-  mojo::BindingSet<apps::mojom::AppService> bindings_;
   // publishers_ is a std::map, not a mojo::InterfacePtrSet, since we want to
   // be able to find *the* publisher for a given apps::mojom::AppType.
   std::map<apps::mojom::AppType, apps::mojom::PublisherPtr> publishers_;
   mojo::InterfacePtrSet<apps::mojom::Subscriber> subscribers_;
 
+  // Must come after the publisher and subscriber maps to ensure it is
+  // destroyed first, closing the connection to avoid dangling callbacks.
+  mojo::BindingSet<apps::mojom::AppService> bindings_;
+
   DISALLOW_COPY_AND_ASSIGN(AppServiceImpl);
 };
 
diff --git a/chrome/test/data/webui/app_management/app_management_browsertest.js b/chrome/test/data/webui/app_management/app_management_browsertest.js
index df89e3cd..1b4db21a 100644
--- a/chrome/test/data/webui/app_management/app_management_browsertest.js
+++ b/chrome/test/data/webui/app_management/app_management_browsertest.js
@@ -5,10 +5,7 @@
 /**
  * @fileoverview Test suite for the App Management page.
  */
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "chrome/common/chrome_features.h"');
 
 function AppManagementBrowserTest() {}
@@ -18,11 +15,12 @@
 
   browsePreload: 'chrome://app-management',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../test_store.js',
     'test_util.js',
     'test_store.js',
-  ]),
+  ],
 
   featureList: ['features::kAppManagement', ''],
 
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
index acd78dd..ca000ed 100644
--- a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
+++ b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
@@ -5,10 +5,7 @@
 /**
  * @fileoverview Test suite for the bookmarks page.
  */
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "chrome/browser/prefs/incognito_mode_prefs.h"');
 GEN('#include "chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h"');
 
@@ -21,13 +18,14 @@
 
   typedefCppFixture: 'BookmarksBrowserTest',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../test_store.js',
     'test_command_manager.js',
     'test_store.js',
     'test_timer_proxy.js',
     'test_util.js',
-  ]),
+  ],
 
   /** override */
   runAccessibilityChecks: true,
@@ -54,7 +52,7 @@
 
   extraLibraries: BookmarksBrowserTest.prototype.extraLibraries.concat([
     'app_test.js',
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+    '//ui/webui/resources/js/util.js',
   ]),
 };
 
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
index ab723c0..c5acc58 100644
--- a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
+++ b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
@@ -7,10 +7,7 @@
  * Should be used for tests which care about focus.
  */
 
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 function BookmarksFocusTest() {}
 
@@ -19,14 +16,15 @@
 
   browsePreload: 'chrome://bookmarks',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+  extraLibraries: [
+    ...PolymerInteractiveUITest.prototype.extraLibraries,
+    '//ui/webui/resources/js/util.js',
     '../settings/test_util.js',
     '../test_store.js',
     'test_command_manager.js',
     'test_store.js',
     'test_util.js',
-  ]),
+  ],
 };
 
 // Web UI interactive tests are flaky on Win10, see https://crbug.com/711256
diff --git a/chrome/test/data/webui/cr_components/cr_components_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_browsertest.js
index 97ac28c..5cd0bfa 100644
--- a/chrome/test/data/webui/cr_components/cr_components_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Tests for shared Polymer components. */
 
-/** @const {string} Path to source root. */
-var ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for shared Polymer components.
@@ -22,9 +18,6 @@
   __proto__: PolymerTest.prototype,
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
-
-  /** @override */
   get browsePreload() {
     throw 'subclasses should override to load a WebUI page that includes it.';
   },
@@ -79,8 +72,8 @@
 
   /** @override */
   extraLibraries: CrComponentsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../fake_chrome_event.js',
     '../chromeos/networking_private_constants.js',
     '../chromeos/fake_networking_private.js',
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
index a5b2076..0ee9fb8 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Tests for shared Polymer elements. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for shared Polymer elements.
@@ -22,9 +18,10 @@
   __proto__: PolymerTest.prototype,
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
-  ]),
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
+    '//ui/webui/resources/js/assert.js',
+  ],
 
   /** @override */
   get browsePreload() {
@@ -396,7 +393,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'chrome/test/data/webui/mock_timer.js',
+    '//chrome/test/data/webui/mock_timer.js',
     'cr_toast_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
index 7945b083..7e87149 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
@@ -4,19 +4,13 @@
 
 /** @fileoverview Tests for shared Polymer elements which rely on focus. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 function CrElementsFocusTest() {}
 
 CrElementsFocusTest.prototype = {
   __proto__: PolymerInteractiveUITest.prototype,
-
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
 };
 
 function CrElementsActionMenuTest() {}
@@ -202,7 +196,7 @@
 
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+    '//ui/webui/resources/js/util.js',
     '../settings/test_util.js',
     'cr_expand_button_focus_tests.js',
   ]),
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
index 3e76d98e..1c2e9c3 100644
--- a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
+++ b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 /**
  * Test fixture for FocusRowBehavior.
@@ -23,11 +19,12 @@
   browsePreload: 'chrome://resources/html/cr/ui/focus_row_behavior.html',
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
+    '//ui/webui/resources/js/util.js',
     'cr_focus_row_behavior_test.js',
     'settings/test_util.js',
-  ]),
+  ],
 
   /** @override */
   setUp: function() {
diff --git a/chrome/test/data/webui/downloads/downloads_browsertest.js b/chrome/test/data/webui/downloads/downloads_browsertest.js
index 672014b..ceb5557 100644
--- a/chrome/test/data/webui/downloads/downloads_browsertest.js
+++ b/chrome/test/data/webui/downloads/downloads_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Tests for the Material Design downloads page. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * @constructor
@@ -27,9 +23,6 @@
   },
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
-
-  /** @override */
   runAccessibilityChecks: true,
 };
 
@@ -47,7 +40,7 @@
 
   /** @override */
   extraLibraries: DownloadsTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/cr.js',
+    '//ui/webui/resources/js/cr.js',
     '../test_browser_proxy.js',
     'test_support.js',
     'item_tests.js',
@@ -72,8 +65,8 @@
 
   /** @override */
   extraLibraries: DownloadsTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/cr.js',
-    ROOT_PATH + 'chrome/browser/resources/downloads/constants.js',
+    '//ui/webui/resources/js/cr.js',
+    '//chrome/browser/resources/downloads/constants.js',
     '../test_browser_proxy.js',
     'test_support.js',
     'manager_tests.js',
diff --git a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
index a7db9e4..c8bcf0b 100644
--- a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
+++ b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
@@ -2,13 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @const {string} Path to root from chrome/test/data/webui/extensions/a11y. */
-const ROOT_PATH = '../../../../../../';
-
 // Polymer BrowserTest fixture and aXe-core accessibility audit.
 GEN_INCLUDE([
-  ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js',
-  ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js',
+  '//chrome/test/data/webui/a11y/accessibility_test.js',
+  '//chrome/test/data/webui/polymer_browser_test_base.js',
 ]);
 GEN('#include "chrome/browser/ui/webui/extensions/' +
     'extension_settings_browsertest.h"');
@@ -24,11 +21,6 @@
     return 'chrome://extensions/';
   }
 
-  // Include files that define the mocha tests.
-  get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH);
-  }
-
   // Default accessibility audit options. Specify in test definition to use.
   static get axeOptions() {
     return {
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 7448723..deb23ef 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer Settings tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "chrome/browser/ui/webui/extensions/' +
     'extension_settings_browsertest.h"');
 
@@ -25,8 +21,9 @@
 
   /** @override */
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
-      ROOT_PATH + 'ui/webui/resources/js/assert.js',
+    return [
+      ...super.extraLibraries,
+      '//ui/webui/resources/js/assert.js',
       'test_util.js',
       '../mock_controller.js',
       '../../../../../ui/webui/resources/js/promise_resolver.js',
@@ -35,7 +32,7 @@
       '../settings/test_util.js',
       '../test_browser_proxy.js',
       'test_service.js',
-    ]);
+    ];
   }
 
   /** @override */
diff --git a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
index 967b27e..b8daf7e 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer Extensions interactive UI tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 GEN('#include "chrome/browser/ui/webui/extensions/' +
     'extension_settings_browsertest.h"');
 
@@ -26,9 +22,10 @@
 
   /** @override */
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
+    return [
+      ...super.extraLibraries,
       '../settings/test_util.js',
-    ]);
+    ];
   }
 };
 
diff --git a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
index 0bb67f2..24575db 100644
--- a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
+++ b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for FindShortcutBehavior.
@@ -28,8 +24,8 @@
 
   /** @override */
   extraLibraries: [
-    ...PolymerTest.getLibraries(ROOT_PATH),
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+    ...PolymerTest.prototype.extraLibraries,
+    '//ui/webui/resources/js/util.js',
     'settings/test_util.js',
     'settings/test_util.js',
     'find_shortcut_behavior_test.js',
diff --git a/chrome/test/data/webui/history/history_browsertest.js b/chrome/test/data/webui/history/history_browsertest.js
index 50a50312b..7b378ef 100644
--- a/chrome/test/data/webui/history/history_browsertest.js
+++ b/chrome/test/data/webui/history/history_browsertest.js
@@ -6,10 +6,7 @@
  * @fileoverview Test suite for the Material Design history page.
  */
 
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "base/command_line.h"');
 GEN('#include "chrome/test/data/webui/history_ui_browsertest.h"');
 
@@ -23,9 +20,10 @@
   /** @override */
   runAccessibilityChecks: false,
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     'test_util.js',
-  ]),
+  ],
 
   /** @override */
   setUp: function() {
diff --git a/chrome/test/data/webui/history/history_focus_test.js b/chrome/test/data/webui/history/history_focus_test.js
index 0a23f83..2e7dc2d 100644
--- a/chrome/test/data/webui/history/history_focus_test.js
+++ b/chrome/test/data/webui/history/history_focus_test.js
@@ -7,10 +7,7 @@
  * Should be used for tests which care about focus.
  */
 
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 function HistoryFocusTest() {}
 
@@ -19,12 +16,13 @@
 
   browsePreload: 'chrome://history',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerInteractiveUITest.prototype.extraLibraries,
     'test_util.js',
-  ]),
+  ],
 
   setUp: function() {
-    PolymerTest.prototype.setUp.call(this);
+    PolymerInteractiveUITest.prototype.setUp.call(this);
 
     suiteSetup(function() {
       // Wait for the top-level app element to be upgraded.
diff --git a/chrome/test/data/webui/management/a11y/management_a11y_test.js b/chrome/test/data/webui/management/a11y/management_a11y_test.js
index 6b8865f1..75842b1 100644
--- a/chrome/test/data/webui/management/a11y/management_a11y_test.js
+++ b/chrome/test/data/webui/management/a11y/management_a11y_test.js
@@ -2,13 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @const {string} Path to root from chrome/test/data/webui/management/a11y. */
-const ROOT_PATH = '../../../../../../';
-
 // Polymer BrowserTest fixture and aXe-core accessibility audit.
 GEN_INCLUDE([
-  ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js',
-  ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js',
+  '//chrome/test/data/webui/a11y/accessibility_test.js',
+  '//chrome/test/data/webui/polymer_browser_test_base.js',
 ]);
 
 GEN('#include "chrome/browser/ui/webui/management_a11y_browsertest.h"');
@@ -24,11 +21,6 @@
     return 'chrome://management/';
   }
 
-  // Include files that define the mocha tests.
-  get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH);
-  }
-
   // Default accessibility audit options. Specify in test definition to use.
   static get axeOptions() {
     return {
diff --git a/chrome/test/data/webui/media_router/media_router_elements_browsertest.js b/chrome/test/data/webui/media_router/media_router_elements_browsertest.js
index 9cfb6a5..d8b9ed2 100644
--- a/chrome/test/data/webui/media_router/media_router_elements_browsertest.js
+++ b/chrome/test/data/webui/media_router/media_router_elements_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Media Router Polymer elements tests. */
 
-/** @const {string} Path to source root. */
-var ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for Media Router Polymer elements.
@@ -32,7 +28,8 @@
   // List tests for individual elements. The media-router-container tests are
   // split between several files and use common functionality from
   // media_router_container_test_base.js.
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     'issue_banner_tests.js',
     'media_router_container_cast_mode_list_tests.js',
     'media_router_container_filter_tests.js',
@@ -45,7 +42,7 @@
     'media_router_search_highlighter_tests.js',
     'route_controls_tests.js',
     'route_details_tests.js',
-  ]),
+  ],
 
   /**
    * Mocks the browser API methods to make them fire events instead.
diff --git a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
index 87e14d0..3eb4217 100644
--- a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
+++ b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Tests for MultiDevice unified setup WebUI. Chrome OS only. */
 
-/** @const {string} Path to source root. */
-var ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for MultiDeviceSetup elements.
@@ -23,7 +19,8 @@
 
   browsePreload: 'chrome://multidevice-setup/',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../test_browser_proxy.js',
     '../fake_chrome_event.js',  // Necessary for fake_quick_unlock_private.js
     '../settings/fake_quick_unlock_private.js',
@@ -31,7 +28,7 @@
     'integration_test.js',
     'setup_succeeded_page_test.js',
     'start_setup_page_test.js',
-  ]),
+  ],
 };
 
 TEST_F('MultiDeviceSetupBrowserTest', 'Integration', function() {
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js
index 7a00117..60b03af5 100644
--- a/chrome/test/data/webui/polymer_browser_test_base.js
+++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -175,15 +175,6 @@
   }
 };
 
-/**
- * Just an alias for PolymerTest.prototype.extraLibraries.
- * TODO(olsen): Remove this function since it is no longer needed - since paths
- * no longer need to be re-written in terms of the ROOT_PATH.
- */
-PolymerTest.getLibraries = function() {
-  return PolymerTest.prototype.extraLibraries;
-};
-
 /*
  * Waits for queued up tasks to finish before proceeding. Inspired by:
  * https://github.com/Polymer/web-component-tester/blob/master/browser/environment/helpers.js#L97
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index aca6d41..3ab107c6 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -4,10 +4,7 @@
 
 /** @fileoverview Runs the Print Preview tests for the new UI. */
 
-const ROOT_PATH = '../../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 const NewPrintPreviewTest = class extends PolymerTest {
   /** @override */
@@ -17,9 +14,10 @@
 
   /** @override */
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
-      ROOT_PATH + 'ui/webui/resources/js/assert.js',
-    ]);
+    return [
+      ...super.extraLibraries,
+      '//ui/webui/resources/js/assert.js',
+    ];
   }
 
   // The name of the mocha suite. Should be overridden by subclasses.
@@ -538,7 +536,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js',
+      '//ui/webui/resources/js/cr/event_target.js',
       '../settings/test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
@@ -682,8 +680,8 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
-      ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js',
+      '//ui/webui/resources/js/web_ui_listener_behavior.js',
+      '//ui/webui/resources/js/cr/event_target.js',
       '../settings/test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
@@ -849,7 +847,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
+      '//ui/webui/resources/js/web_ui_listener_behavior.js',
       '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
@@ -1230,7 +1228,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
+      '//ui/webui/resources/js/web_ui_listener_behavior.js',
       '../test_browser_proxy.js',
       '../settings/test_util.js',
       'cloud_print_interface_stub.js',
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
index f5d18636..73df732c 100644
--- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer Print Preview interactive UI tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 const PrintPreviewInteractiveUITest = class extends PolymerInteractiveUITest {
   /** @override */
@@ -19,9 +15,10 @@
 
   /** @override */
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
-      ROOT_PATH + 'ui/webui/resources/js/assert.js',
-    ]);
+    return [
+      ...super.extraLibraries,
+      '//ui/webui/resources/js/assert.js',
+    ];
   }
 
   // The name of the mocha suite. Should be overridden by subclasses.
@@ -45,7 +42,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '//chrome/test/data/webui/settings/test_util.js',
       'print_header_interactive_test.js',
     ]);
   }
@@ -79,7 +76,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '//chrome/test/data/webui/settings/test_util.js',
       'button_strip_interactive_test.js',
     ]);
   }
@@ -113,8 +110,8 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
-      ROOT_PATH + 'ui/webui/resources/js/web_ui_listener_behavior.js',
+      '//chrome/test/data/webui/settings/test_util.js',
+      '//ui/webui/resources/js/web_ui_listener_behavior.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'print_preview_test_utils.js',
@@ -256,7 +253,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'ui/webui/resources/js/util.js',
+      '//ui/webui/resources/js/util.js',
       '../settings/test_util.js',
       'print_preview_test_utils.js',
       'scaling_settings_interactive_test.js',
diff --git a/chrome/test/data/webui/resources/webui_resources_browsertest.js b/chrome/test/data/webui/resources/webui_resources_browsertest.js
index 63516986..a45306f8 100644
--- a/chrome/test/data/webui/resources/webui_resources_browsertest.js
+++ b/chrome/test/data/webui/resources/webui_resources_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the WebUI resources tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * Test fixture for Polymer Settings elements.
@@ -27,9 +23,6 @@
   },
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
-
-  /** @override */
   setUp: function() {
     PolymerTest.prototype.setUp.call(this);
   },
diff --git a/chrome/test/data/webui/set_time_dialog_browsertest.js b/chrome/test/data/webui/set_time_dialog_browsertest.js
index 6b57724b..a18e570a 100644
--- a/chrome/test/data/webui/set_time_dialog_browsertest.js
+++ b/chrome/test/data/webui/set_time_dialog_browsertest.js
@@ -2,11 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../';
-
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * SetTimeDialogBrowserTest tests the "Set Time" web UI dialog.
@@ -20,9 +16,10 @@
 
   browsePreload: 'chrome://set-time/',
 
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
-    ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js',
-  ]),
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
+    '//chrome/test/data/webui/test_browser_proxy.js',
+  ],
 };
 
 TEST_F('SetTimeDialogBrowserTest', 'All', function() {
diff --git a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
index da11a7ab..7d52614 100644
--- a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
+++ b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
@@ -4,13 +4,10 @@
 
 /** @fileoverview Runs the Polymer Accessibility Settings tests. */
 
-/** @const {string} Path to root from chrome/test/data/webui/settings/a11y. */
-const ROOT_PATH = '../../../../../../';
-
 // Polymer BrowserTest fixture and aXe-core accessibility audit.
 GEN_INCLUDE([
-  ROOT_PATH + 'chrome/test/data/webui/a11y/accessibility_test.js',
-  ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js',
+  '//chrome/test/data/webui/a11y/accessibility_test.js',
+  '//chrome/test/data/webui/polymer_browser_test_base.js',
 ]);
 
 /**
@@ -58,9 +55,10 @@
   browsePreload: 'chrome://settings/',
 
   // Include files that define the mocha tests.
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../ensure_lazy_loaded.js',
-  ]),
+  ],
 
   // TODO(hcarmona): Remove once ADT is not longer in the testing infrastructure
   runAccessibilityChecks: false,
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 086ed3c..8e7bd966 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer Settings tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 GEN('#if defined(OS_CHROMEOS)');
 GEN('#include "ash/public/cpp/ash_features.h"');
@@ -35,9 +31,10 @@
   },
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     'ensure_lazy_loaded.js',
-  ]),
+  ],
 
   /** @override */
   setUp: function() {
@@ -767,7 +764,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'appearance_page_test.js',
   ]),
@@ -791,7 +788,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'appearance_fonts_page_test.js',
   ]),
@@ -867,7 +864,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'downloads_page_test.js',
   ]),
@@ -1033,7 +1030,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     'test_util.js',
     '../test_browser_proxy.js',
     'test_privacy_page_browser_proxy.js',
@@ -1067,7 +1064,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     'test_util.js',
     '../test_browser_proxy.js',
     'test_privacy_page_browser_proxy.js',
@@ -1505,7 +1502,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/assert.js',
     '../fake_chrome_event.js',
     'fake_settings_private.js',
     'fake_system_display.js',
@@ -1552,7 +1549,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/assert.js',
     '../fake_chrome_event.js',
     'fake_bluetooth.js',
     'fake_bluetooth_private.js',
@@ -1579,8 +1576,8 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/assert.js',
     '../fake_chrome_event.js',
     '../chromeos/fake_networking_private.js',
     '../chromeos/cr_onc_strings.js',
@@ -1607,9 +1604,9 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
-    ROOT_PATH + 'ui/webui/resources/js/util.js',
+    '//ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/util.js',
     '../fake_chrome_event.js',
     '../chromeos/fake_networking_private.js',
     '../chromeos/cr_onc_strings.js',
@@ -1761,7 +1758,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../fake_chrome_event.js',
     'test_util.js',
     '../test_browser_proxy.js',
@@ -2055,7 +2052,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/assert.js',
+    '//ui/webui/resources/js/assert.js',
     'test_util.js',
     '../test_browser_proxy.js',
     'cups_printer_page_tests.js',
@@ -2266,7 +2263,7 @@
   featureList: ['features::kCrostini', ''],
 
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'test_crostini_browser_proxy.js',
     'crostini_page_test.js',
@@ -2291,7 +2288,7 @@
   browsePreload: 'chrome://settings/plugin_vm_page/plugin_vm_page.html',
 
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'plugin_vm_page_test.js',
   ]),
@@ -2315,7 +2312,7 @@
   browsePreload: 'chrome://settings/android_apps_page/android_apps_page.html',
 
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'test_android_apps_browser_proxy.js',
     'android_apps_page_test.js',
@@ -2369,7 +2366,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
     'google_assistant_page_test.js',
   ]),
diff --git a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
index a297ff7b..36a5fde7 100644
--- a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
+++ b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer Settings interactive UI tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
 
 /**
  * Test fixture for interactive Polymer Settings elements.
@@ -27,9 +23,6 @@
   },
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
-
-  /** @override */
   setUp: function() {
     PolymerTest.prototype.setUp.call(this);
     // We aren't loading the main document.
diff --git a/chrome/test/data/webui/settings/help_page_browsertest.js b/chrome/test/data/webui/settings/help_page_browsertest.js
index 7e09fd55..09ff3cb 100644
--- a/chrome/test/data/webui/settings/help_page_browsertest.js
+++ b/chrome/test/data/webui/settings/help_page_browsertest.js
@@ -19,9 +19,6 @@
   browsePreload: 'chrome://help/',
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
-
-  /** @override */
   setUp: function() {
     // Intentionally bypassing SettingsPageBrowserTest#setUp.
     PolymerTest.prototype.setUp.call(this);
diff --git a/chrome/test/data/webui/settings/settings_page_browsertest.js b/chrome/test/data/webui/settings/settings_page_browsertest.js
index ccc8616..f7c1323b 100644
--- a/chrome/test/data/webui/settings/settings_page_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_page_browsertest.js
@@ -4,13 +4,8 @@
 
 /** @fileoverview Prototype for Settings page tests. */
 
-/** @const {string} Path to root from chrome/test/data/webui/settings/. */
-// eslint-disable-next-line no-var
-var ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
 /**
  * @constructor
@@ -25,10 +20,11 @@
   browsePreload: 'chrome://settings/',
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../fake_chrome_event.js',
     'fake_settings_private.js',
-  ]),
+  ],
 
   /** @type {?SettingsBasicPageElement} */
   basicPage: null,
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js
index b5e367a..ec4c5e9 100644
--- a/chrome/test/data/webui/signin/signin_browsertest.js
+++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Sign-in web UI tests. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "base/command_line.h"');
 GEN('#include "chrome/test/data/webui/signin_browsertest.h"');
 
@@ -36,13 +32,14 @@
 
   /** @override */
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
-      ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js',
-      ROOT_PATH + 'chrome/browser/resources/signin/dice_sync_confirmation/' +
+    return [
+      ...super.extraLibraries,
+      '//chrome/test/data/webui/test_browser_proxy.js',
+      '//chrome/browser/resources/signin/dice_sync_confirmation/' +
           'sync_confirmation_browser_proxy.js',
       'test_sync_confirmation_browser_proxy.js',
       'sync_confirmation_test.js',
-    ]);
+    ];
   }
 };
 
diff --git a/chrome/test/data/webui/user_manager/user_manager_browsertest.js b/chrome/test/data/webui/user_manager/user_manager_browsertest.js
index 4e92b851..f8f22c4b 100644
--- a/chrome/test/data/webui/user_manager/user_manager_browsertest.js
+++ b/chrome/test/data/webui/user_manager/user_manager_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Tests for the Material Design user manager page. */
 
-/** @const {string} Path to root from chrome/test/data/webui/user_manager/ */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "chrome/common/chrome_features.h"');
 
 /**
@@ -25,13 +21,14 @@
   browsePreload: 'chrome://md-user-manager/',
 
   /** @override */
-  extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+  extraLibraries: [
+    ...PolymerTest.prototype.extraLibraries,
     '../test_browser_proxy.js',
     'control_bar_tests.js',
     'create_profile_tests.js',
     'test_profile_browser_proxy.js',
     'user_manager_pages_tests.js',
-  ]),
+  ],
 };
 
 GEN('#if defined(OS_WIN)');
diff --git a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
index 48095d3..3ae01aba 100644
--- a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
+++ b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
@@ -4,12 +4,8 @@
 
 /** @fileoverview Runs the Polymer welcome tests on onboarding-welcome UI. */
 
-/** @const {string} Path to source root. */
-const ROOT_PATH = '../../../../../';
-
 // Polymer BrowserTest fixture.
-GEN_INCLUDE(
-    [ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 GEN('#include "chrome/browser/ui/webui/welcome/nux_helper.h"');
 
 /**
@@ -22,9 +18,10 @@
   }
 
   get extraLibraries() {
-    return PolymerTest.getLibraries(ROOT_PATH).concat([
+    return [
+      ...super.extraLibraries,
       '../test_browser_proxy.js',
-    ]);
+    ];
   }
 
   /** @override */
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
index 74e2c10..e1b8a9e 100644
--- a/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
+++ b/chromeos/services/assistant/platform/audio_output_provider_impl_unittest.cc
@@ -81,7 +81,8 @@
  public:
   AudioDeviceOwnerTest()
       : task_env_(base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
-                  base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {}
+                  base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::
+                      QUEUED) {}
 
   ~AudioDeviceOwnerTest() override { task_env_.RunUntilIdle(); }
 
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn
index 79ed570..f722f4c 100644
--- a/components/arc/common/BUILD.gn
+++ b/components/arc/common/BUILD.gn
@@ -45,6 +45,7 @@
       "policy.mojom",
       "power.mojom",
       "print.mojom",
+      "print_spooler.mojom",
       "process.mojom",
       "property.mojom",
       "rotation_lock.mojom",
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom
index 64b73c9d..7a2121f 100644
--- a/components/arc/common/arc_bridge.mojom
+++ b/components/arc/common/arc_bridge.mojom
@@ -36,6 +36,7 @@
 import "components/arc/common/policy.mojom";
 import "components/arc/common/power.mojom";
 import "components/arc/common/print.mojom";
+import "components/arc/common/print_spooler.mojom";
 import "components/arc/common/process.mojom";
 import "components/arc/common/property.mojom";
 import "components/arc/common/rotation_lock.mojom";
@@ -52,9 +53,9 @@
 import "components/arc/common/wake_lock.mojom";
 import "components/arc/common/wallpaper.mojom";
 
-// Next MinVersion: 45
+// Next MinVersion: 46
 // Deprecated method IDs: 101, 105
-// Next method ID: 150
+// Next method ID: 151
 interface ArcBridgeHost {
   // Keep the entries alphabetical. In order to do so without breaking
   // compatibility with the ARC instance, explicitly assign each interface a
@@ -165,6 +166,10 @@
   // Notifies Chrome that the PrintInstance interface is ready.
   [MinVersion=16] OnPrintInstanceReady@121(PrintInstance instance_ptr);
 
+  // Notifies Chrome that the PrintSpoolerInstance interface is ready.
+  [MinVersion=45] OnPrintSpoolerInstanceReady@150(
+                      PrintSpoolerInstance instance_ptr);
+
   // Notifies Chrome that the ProcessInstance interface is ready.
   OnProcessInstanceReady@104(ProcessInstance instance_ptr);
 
diff --git a/components/arc/common/print_spooler.mojom b/components/arc/common/print_spooler.mojom
new file mode 100644
index 0000000..81c415b
--- /dev/null
+++ b/components/arc/common/print_spooler.mojom
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Next MinVersion: 1
+
+module arc.mojom;
+
+// Next method ID: 0
+interface PrintSpoolerHost {
+  // TODO(jschettler): Add methods to open and close print preview
+};
+
+// Next method ID: 1
+interface PrintSpoolerInstance {
+  // Establishes full-duplex communication with the host.
+  [MinVersion=0] Init@0(PrintSpoolerHost host_ptr) => ();
+};
\ No newline at end of file
diff --git a/components/arc/session/arc_bridge_host_impl.cc b/components/arc/session/arc_bridge_host_impl.cc
index f90cacd..16bdd5f 100644
--- a/components/arc/session/arc_bridge_host_impl.cc
+++ b/components/arc/session/arc_bridge_host_impl.cc
@@ -42,6 +42,7 @@
 #include "components/arc/common/policy.mojom.h"
 #include "components/arc/common/power.mojom.h"
 #include "components/arc/common/print.mojom.h"
+#include "components/arc/common/print_spooler.mojom.h"
 #include "components/arc/common/process.mojom.h"
 #include "components/arc/common/property.mojom.h"
 #include "components/arc/common/rotation_lock.mojom.h"
@@ -256,6 +257,12 @@
   OnInstanceReady(arc_bridge_service_->print(), std::move(print_ptr));
 }
 
+void ArcBridgeHostImpl::OnPrintSpoolerInstanceReady(
+    mojom::PrintSpoolerInstancePtr print_spooler_ptr) {
+  OnInstanceReady(arc_bridge_service_->print_spooler(),
+                  std::move(print_spooler_ptr));
+}
+
 void ArcBridgeHostImpl::OnProcessInstanceReady(
     mojom::ProcessInstancePtr process_ptr) {
   OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr));
diff --git a/components/arc/session/arc_bridge_host_impl.h b/components/arc/session/arc_bridge_host_impl.h
index 317afea..7f1d3fa 100644
--- a/components/arc/session/arc_bridge_host_impl.h
+++ b/components/arc/session/arc_bridge_host_impl.h
@@ -87,6 +87,8 @@
   void OnPolicyInstanceReady(mojom::PolicyInstancePtr policy_ptr) override;
   void OnPowerInstanceReady(mojom::PowerInstancePtr power_ptr) override;
   void OnPrintInstanceReady(mojom::PrintInstancePtr print_ptr) override;
+  void OnPrintSpoolerInstanceReady(
+      mojom::PrintSpoolerInstancePtr print_spooler_ptr) override;
   void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override;
   void OnPropertyInstanceReady(
       mojom::PropertyInstancePtr property_ptr) override;
diff --git a/components/arc/session/arc_bridge_service.cc b/components/arc/session/arc_bridge_service.cc
index 1a4ecc7f..cc3791bb 100644
--- a/components/arc/session/arc_bridge_service.cc
+++ b/components/arc/session/arc_bridge_service.cc
@@ -37,6 +37,7 @@
 #include "components/arc/common/policy.mojom.h"
 #include "components/arc/common/power.mojom.h"
 #include "components/arc/common/print.mojom.h"
+#include "components/arc/common/print_spooler.mojom.h"
 #include "components/arc/common/process.mojom.h"
 #include "components/arc/common/property.mojom.h"
 #include "components/arc/common/rotation_lock.mojom.h"
diff --git a/components/arc/session/arc_bridge_service.h b/components/arc/session/arc_bridge_service.h
index 5f590ab3..31f2e311 100644
--- a/components/arc/session/arc_bridge_service.h
+++ b/components/arc/session/arc_bridge_service.h
@@ -71,6 +71,8 @@
 class PowerInstance;
 class PrintHost;
 class PrintInstance;
+class PrintSpoolerHost;
+class PrintSpoolerInstance;
 class ProcessInstance;
 class PropertyInstance;
 class RotationLockInstance;
@@ -204,6 +206,10 @@
   ConnectionHolder<mojom::PrintInstance, mojom::PrintHost>* print() {
     return &print_;
   }
+  ConnectionHolder<mojom::PrintSpoolerInstance, mojom::PrintSpoolerHost>*
+  print_spooler() {
+    return &print_spooler_;
+  }
   ConnectionHolder<mojom::ProcessInstance>* process() { return &process_; }
   ConnectionHolder<mojom::PropertyInstance>* property() { return &property_; }
   ConnectionHolder<mojom::RotationLockInstance>* rotation_lock() {
@@ -282,6 +288,8 @@
   ConnectionHolder<mojom::PolicyInstance, mojom::PolicyHost> policy_;
   ConnectionHolder<mojom::PowerInstance, mojom::PowerHost> power_;
   ConnectionHolder<mojom::PrintInstance, mojom::PrintHost> print_;
+  ConnectionHolder<mojom::PrintSpoolerInstance, mojom::PrintSpoolerHost>
+      print_spooler_;
   ConnectionHolder<mojom::ProcessInstance> process_;
   ConnectionHolder<mojom::PropertyInstance> property_;
   ConnectionHolder<mojom::RotationLockInstance> rotation_lock_;
diff --git a/components/arc/test/fake_arc_bridge_host.cc b/components/arc/test/fake_arc_bridge_host.cc
index 5b52f52..4f4247b5 100644
--- a/components/arc/test/fake_arc_bridge_host.cc
+++ b/components/arc/test/fake_arc_bridge_host.cc
@@ -37,6 +37,7 @@
 #include "components/arc/common/policy.mojom.h"
 #include "components/arc/common/power.mojom.h"
 #include "components/arc/common/print.mojom.h"
+#include "components/arc/common/print_spooler.mojom.h"
 #include "components/arc/common/process.mojom.h"
 #include "components/arc/common/property.mojom.h"
 #include "components/arc/common/rotation_lock.mojom.h"
@@ -150,6 +151,9 @@
 void FakeArcBridgeHost::OnPrintInstanceReady(
     mojom::PrintInstancePtr print_ptr) {}
 
+void FakeArcBridgeHost::OnPrintSpoolerInstanceReady(
+    mojom::PrintSpoolerInstancePtr print_spooler_ptr) {}
+
 void FakeArcBridgeHost::OnProcessInstanceReady(
     mojom::ProcessInstancePtr process_ptr) {}
 
diff --git a/components/arc/test/fake_arc_bridge_host.h b/components/arc/test/fake_arc_bridge_host.h
index b704a5b..114e8a2 100644
--- a/components/arc/test/fake_arc_bridge_host.h
+++ b/components/arc/test/fake_arc_bridge_host.h
@@ -67,6 +67,8 @@
   void OnPolicyInstanceReady(mojom::PolicyInstancePtr policy_ptr) override;
   void OnPowerInstanceReady(mojom::PowerInstancePtr power_ptr) override;
   void OnPrintInstanceReady(mojom::PrintInstancePtr print_ptr) override;
+  void OnPrintSpoolerInstanceReady(
+      mojom::PrintSpoolerInstancePtr print_spooler_ptr) override;
   void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override;
   void OnPropertyInstanceReady(
       mojom::PropertyInstancePtr property_ptr) override;
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 4439416..3dc8cf20 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -2249,6 +2249,57 @@
                     "text", response_data.fields[3]);
 }
 
+// Test that if a company is of a format of a birthyear and the relevant feature
+// is enabled, we would not fill it.
+TEST_F(AutofillManagerTest, FillAddressForm_CompanyBirthyear) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kAutofillRejectCompanyBirthyear);
+
+  // Set up our form data.
+  FormData address_form;
+  address_form.name = ASCIIToUTF16("MyForm");
+  address_form.url = GURL("https://myform.com/form.html");
+  address_form.action = GURL("https://myform.com/submit.html");
+
+  FormFieldData field;
+  test::CreateTestFormField("First name", "firstname", "", "text", &field);
+  address_form.fields.push_back(field);
+  test::CreateTestFormField("Middle name", "middle", "", "text", &field);
+  address_form.fields.push_back(field);
+  test::CreateTestFormField("Last name", "lastname", "", "text", &field);
+  address_form.fields.push_back(field);
+  test::CreateTestFormField("Company", "company", "", "text", &field);
+  address_form.fields.push_back(field);
+
+  std::vector<FormData> address_forms(1, address_form);
+  FormsSeen(address_forms);
+
+  AutofillProfile profile;
+  const char guid[] = "00000000-0000-0000-0000-000000000123";
+  test::SetProfileInfo(&profile, "Elvis", "Aaron", "Presley",
+                       "theking@gmail.com", "1987", "3734 Elvis Presley Blvd.",
+                       "Apt. 10", "Memphis", "Tennessee", "38116", "US",
+                       "12345678901");
+  profile.set_guid(guid);
+  personal_data_.AddProfile(profile);
+
+  int response_page_id = 0;
+  FormData response_data;
+  FillAutofillFormDataAndSaveResults(
+      kDefaultPageID, address_form, *address_form.fields.begin(),
+      MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+
+  // All the fields should be filled except the company.
+  ExpectFilledField("First name", "firstname", "Elvis", "text",
+                    response_data.fields[0]);
+  ExpectFilledField("Middle name", "middle", "Aaron", "text",
+                    response_data.fields[1]);
+  ExpectFilledField("Last name", "lastname", "Presley", "text",
+                    response_data.fields[2]);
+  ExpectFilledField("Company", "company", "", "text", response_data.fields[3]);
+}
+
 // Test that a field with a value equal to it's placeholder attribute is filled.
 TEST_F(AutofillManagerTest, FillAddressForm_PlaceholderEqualsValue) {
   FormData address_form;
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc
index 97c62222..504acbfa 100644
--- a/components/autofill/core/browser/autofill_profile.cc
+++ b/components/autofill/core/browser/autofill_profile.cc
@@ -228,6 +228,7 @@
 AutofillProfile::AutofillProfile(const std::string& guid,
                                  const std::string& origin)
     : AutofillDataModel(guid, origin),
+      company_(this),
       phone_number_(this),
       record_type_(LOCAL_PROFILE),
       has_converted_(false),
@@ -235,6 +236,7 @@
 
 AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id)
     : AutofillDataModel(base::GenerateGUID(), std::string()),
+      company_(this),
       phone_number_(this),
       server_id_(server_id),
       record_type_(type),
@@ -245,6 +247,7 @@
 
 AutofillProfile::AutofillProfile()
     : AutofillDataModel(base::GenerateGUID(), std::string()),
+      company_(this),
       phone_number_(this),
       record_type_(LOCAL_PROFILE),
       has_converted_(false),
@@ -252,6 +255,7 @@
 
 AutofillProfile::AutofillProfile(const AutofillProfile& profile)
     : AutofillDataModel(std::string(), std::string()),
+      company_(this),
       phone_number_(this),
       weak_ptr_factory_(this) {
   operator=(profile);
@@ -277,6 +281,7 @@
   name_ = profile.name_;
   email_ = profile.email_;
   company_ = profile.company_;
+  company_.set_profile(this);
   phone_number_ = profile.phone_number_;
   phone_number_.set_profile(this);
 
@@ -451,10 +456,12 @@
 }
 
 bool AutofillProfile::EqualsForUpdatePurposes(
-    const AutofillProfile& profile) const {
-  return use_count() == profile.use_count() &&
-         UseDateEqualsInSeconds(&profile) &&
-         language_code() == profile.language_code() && Compare(profile) == 0;
+    const AutofillProfile& new_profile) const {
+  return use_count() == new_profile.use_count() &&
+         (origin() == new_profile.origin() || !new_profile.IsVerified()) &&
+         UseDateEqualsInSeconds(&new_profile) &&
+         language_code() == new_profile.language_code() &&
+         Compare(new_profile) == 0;
 }
 
 bool AutofillProfile::EqualsForClientValidationPurpose(
@@ -559,7 +566,7 @@
 
   NameInfo name;
   EmailInfo email;
-  CompanyInfo company;
+  CompanyInfo company(this);
   PhoneNumber phone_number(this);
   Address address;
 
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h
index 3f45fbb..4fec90b 100644
--- a/components/autofill/core/browser/autofill_profile.h
+++ b/components/autofill/core/browser/autofill_profile.h
@@ -104,7 +104,10 @@
   // differences in usage stats.
   bool EqualsForSyncPurposes(const AutofillProfile& profile) const;
 
-  bool EqualsForUpdatePurposes(const AutofillProfile& profile) const;
+  // Returns true if |new_profile| and this are considered equal for updating
+  // purposes, meaning that if equal we do not need to update this profile to
+  // the |new_profile|.
+  bool EqualsForUpdatePurposes(const AutofillProfile& new_profile) const;
 
   // Compares the values of kSupportedTypesByClientForValidation fields.
   bool EqualsForClientValidationPurpose(const AutofillProfile& profile) const;
diff --git a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
index 36da4fdf..0fac040 100644
--- a/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
@@ -6,11 +6,13 @@
 
 #include "base/guid.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/contact_info.h"
 #include "components/autofill/core/browser/country_names.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 // Field Type Constants
@@ -758,9 +760,16 @@
 }
 
 TEST_F(AutofillProfileComparatorTest, MergeCompanyNames) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitWithFeatures(
+      /*enabled_features=*/{autofill::features::
+                                kAutofillRejectCompanyBirthyear},
+      /*disabled_features=*/{});
+
   static const char kCompanyA[] = "Some Company";
   static const char kCompanyB[] = "SÔMÈ ÇÖMPÁÑÝ";
   static const char kCompanyC[] = "SÔMÈ ÇÖMPÁÑÝ A.G.";
+  static const char kCompanyD[] = "1987";
 
   CompanyInfo company_a;
   company_a.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyA));
@@ -781,15 +790,31 @@
   AutofillProfile profile_c = CreateProfileWithCompanyName(kCompanyC);
   profile_c.set_use_date(profile_a.use_date() - base::TimeDelta::FromDays(1));
 
+  // Company Name D is in the format of a birthyear, invalid and non-verified.
+  CompanyInfo company_d;
+  company_d.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyD));
+  AutofillProfile profile_d = CreateProfileWithCompanyName(kCompanyD);
+  profile_a.set_use_date(base::Time::Now());
+
   MergeCompanyNamesAndExpect(profile_a, profile_a, company_a);
   MergeCompanyNamesAndExpect(profile_a, profile_b, company_b);
   MergeCompanyNamesAndExpect(profile_a, profile_c, company_c);
+  MergeCompanyNamesAndExpect(profile_a, profile_d, company_a);
+
   MergeCompanyNamesAndExpect(profile_b, profile_a, company_b);
   MergeCompanyNamesAndExpect(profile_b, profile_b, company_b);
   MergeCompanyNamesAndExpect(profile_b, profile_c, company_c);
+  MergeCompanyNamesAndExpect(profile_b, profile_d, company_b);
+
   MergeCompanyNamesAndExpect(profile_c, profile_a, company_c);
   MergeCompanyNamesAndExpect(profile_c, profile_b, company_c);
   MergeCompanyNamesAndExpect(profile_c, profile_c, company_c);
+  MergeCompanyNamesAndExpect(profile_c, profile_d, company_c);
+
+  MergeCompanyNamesAndExpect(profile_d, profile_a, company_a);
+  MergeCompanyNamesAndExpect(profile_d, profile_b, company_b);
+  MergeCompanyNamesAndExpect(profile_d, profile_c, company_c);
+  MergeCompanyNamesAndExpect(profile_d, profile_d, company_d);
 }
 
 TEST_F(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
diff --git a/components/autofill/core/browser/contact_info.cc b/components/autofill/core/browser/contact_info.cc
index c2d5104..3f8dc70 100644
--- a/components/autofill/core/browser/contact_info.cc
+++ b/components/autofill/core/browser/contact_info.cc
@@ -13,9 +13,11 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
+#include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_l10n_util.h"
+#include "components/autofill/core/common/autofill_regexes.h"
 
 namespace autofill {
 
@@ -218,6 +220,8 @@
 
 CompanyInfo::CompanyInfo() {}
 
+CompanyInfo::CompanyInfo(const AutofillProfile* profile) : profile_(profile) {}
+
 CompanyInfo::CompanyInfo(const CompanyInfo& info) {
   *this = info;
 }
@@ -228,12 +232,13 @@
   if (this == &info)
     return *this;
 
-  company_name_ = info.company_name_;
+  company_name_ = info.GetRawInfo(COMPANY_NAME);
   return *this;
 }
 
 bool CompanyInfo::operator==(const CompanyInfo& other) const {
-  return this == &other || company_name_ == other.company_name_;
+  return this == &other ||
+         GetRawInfo(COMPANY_NAME) == other.GetRawInfo(COMPANY_NAME);
 }
 
 void CompanyInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
@@ -241,7 +246,7 @@
 }
 
 base::string16 CompanyInfo::GetRawInfo(ServerFieldType type) const {
-  return company_name_;
+  return IsValidOrVerified(company_name_) ? company_name_ : base::string16();
 }
 
 void CompanyInfo::SetRawInfo(ServerFieldType type,
@@ -250,4 +255,16 @@
   company_name_ = value;
 }
 
+bool CompanyInfo::IsValidOrVerified(const base::string16& value) const {
+  if (!base::FeatureList::IsEnabled(
+          autofill::features::kAutofillRejectCompanyBirthyear))
+    return true;
+
+  if (profile_ && profile_->IsVerified())
+    return true;
+
+  // Companies that are of the format of a four digit birth year are not valid.
+  return !MatchesPattern(value, base::UTF8ToUTF16("^(19|20)\\d{2}$"));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/contact_info.h b/components/autofill/core/browser/contact_info.h
index 3c5e451a..3bb79c4 100644
--- a/components/autofill/core/browser/contact_info.h
+++ b/components/autofill/core/browser/contact_info.h
@@ -13,6 +13,8 @@
 
 namespace autofill {
 
+class AutofillProfile;
+
 // A form group that stores name information.
 class NameInfo : public FormGroup {
  public:
@@ -91,6 +93,7 @@
  public:
   CompanyInfo();
   CompanyInfo(const CompanyInfo& info);
+  explicit CompanyInfo(const AutofillProfile* profile);
   ~CompanyInfo() override;
 
   CompanyInfo& operator=(const CompanyInfo& info);
@@ -100,12 +103,15 @@
   // FormGroup:
   base::string16 GetRawInfo(ServerFieldType type) const override;
   void SetRawInfo(ServerFieldType type, const base::string16& value) override;
+  void set_profile(const AutofillProfile* profile) { profile_ = profile; }
 
  private:
   // FormGroup:
   void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override;
+  bool IsValidOrVerified(const base::string16& value) const;
 
   base::string16 company_name_;
+  const AutofillProfile* profile_ = nullptr;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/contact_info_unittest.cc b/components/autofill/core/browser/contact_info_unittest.cc
index 3f2502fa..cac7c92 100644
--- a/components/autofill/core/browser/contact_info_unittest.cc
+++ b/components/autofill/core/browser/contact_info_unittest.cc
@@ -11,8 +11,11 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::ASCIIToUTF16;
@@ -397,4 +400,96 @@
                     NamePartsAreEmptyTestCase{"", "Mitchell", "", "", false},
                     NamePartsAreEmptyTestCase{"", "", "Morrison", "", false}));
 
+TEST(CompanyTest, CompanyNameYear) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear},
+      /*disabled_features=*/{});
+
+  AutofillProfile profile;
+  CompanyInfo company(&profile);
+  ASSERT_FALSE(profile.IsVerified());
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google"));
+  EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987"));
+  EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("It was 1987."));
+  EXPECT_EQ(UTF8ToUTF16("It was 1987."), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987 was the year."));
+  EXPECT_EQ(UTF8ToUTF16("1987 was the year."),
+            company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Yes, 1987 was the year."));
+  EXPECT_EQ(UTF8ToUTF16("Yes, 1987 was the year."),
+            company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019"));
+  EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818"));
+  EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345"));
+  EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME));
+
+  profile.set_origin("Not empty");
+  ASSERT_TRUE(profile.IsVerified());
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google"));
+  EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987"));
+  EXPECT_EQ(UTF8ToUTF16("1987"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019"));
+  EXPECT_EQ(UTF8ToUTF16("2019"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818"));
+  EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME));
+
+  company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345"));
+  EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME));
+}
+
+TEST(CompanyTest, CompanyNameYearCopy) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear},
+      /*disabled_features=*/{});
+
+  AutofillProfile profile;
+  ASSERT_FALSE(profile.IsVerified());
+
+  CompanyInfo company_google(&profile);
+  CompanyInfo company_year(&profile);
+
+  company_google.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google"));
+  company_year.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987"));
+
+  company_google = company_year;
+  EXPECT_EQ(UTF8ToUTF16(""), company_google.GetRawInfo(COMPANY_NAME));
+}
+
+TEST(CompanyTest, CompanyNameYearIsEqual) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear},
+      /*disabled_features=*/{});
+
+  AutofillProfile profile;
+  ASSERT_FALSE(profile.IsVerified());
+
+  CompanyInfo company_old(&profile);
+  CompanyInfo company_young(&profile);
+
+  company_old.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019"));
+  company_young.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987"));
+
+  EXPECT_EQ(company_old, company_young);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.cc b/components/autofill/core/browser/payments/local_card_migration_manager.cc
index a920209..9103db1 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager.cc
+++ b/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -209,6 +209,7 @@
     migration_request_.context_token = context_token;
     legal_message_ = base::DictionaryValue::From(std::move(legal_message));
     migration_request_.risk_data.clear();
+    supported_card_bin_ranges_ = supported_card_bin_ranges;
     // If we successfully received the legal docs, trigger the offer-to-migrate
     // dialog. If triggered from settings page, we pop-up the main prompt
     // directly. If not, we pop up the intermediate bubble.
@@ -219,6 +220,12 @@
       // Pops up a larger, modal dialog showing the local cards to be uploaded.
       ShowMainMigrationDialog();
     } else {
+      // Filter the migratable credit cards with |supported_card_bin_ranges_|.
+      FilterOutUnsupportedLocalCards();
+      // Abandon the migration if no supported card left.
+      // TODO(crbug.com/954367): Log a metric here.
+      if (migratable_credit_cards_.empty())
+        return;
       client_->ShowLocalCardMigrationDialog(base::BindOnce(
           &LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog,
           weak_ptr_factory_.GetWeakPtr()));
@@ -383,6 +390,28 @@
       migratable_credit_cards_.push_back(MigratableCreditCard(*credit_card));
     }
   }
+
+  // Filter out Unsupported local cards when |supported_card_bin_ranges_| is not
+  // empty.
+  FilterOutUnsupportedLocalCards();
+}
+
+void LocalCardMigrationManager::FilterOutUnsupportedLocalCards() {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillDoNotMigrateUnsupportedLocalCards) &&
+      !supported_card_bin_ranges_.empty()) {
+    // Update the |migratable_credit_cards_| with the
+    // |supported_card_bin_ranges|. This will remove any card from
+    // |migratable_credit_cards_| of which the card number is not in
+    // |supported_card_bin_ranges|.
+    auto card_is_unsupported =
+        [& supported_card_bin_ranges =
+             supported_card_bin_ranges_](MigratableCreditCard& card) {
+          return !payments::IsCreditCardSupported(card.credit_card(),
+                                                  supported_card_bin_ranges);
+        };
+    base::EraseIf(migratable_credit_cards_, card_is_unsupported);
+  }
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.h b/components/autofill/core/browser/payments/local_card_migration_manager.h
index 9c12b4e..1126f97b 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager.h
+++ b/components/autofill/core/browser/payments/local_card_migration_manager.h
@@ -156,6 +156,11 @@
  private:
   friend class LocalCardMigrationBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest,
+                           MigrateCreditCard_MigrateWhenHasSupportedLocalCard);
+  FRIEND_TEST_ALL_PREFIXES(
+      LocalCardMigrationManagerTest,
+      MigrateCreditCard_MigrationAbortWhenNoSupportedCards);
+  FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest,
                            MigrateCreditCard_MigrationPermanentFailure);
   FRIEND_TEST_ALL_PREFIXES(LocalCardMigrationManagerTest,
                            MigrateCreditCard_MigrationTemporaryFailure);
@@ -167,6 +172,10 @@
   // Returns the LocalCardMigrationStrikeDatabase for |client_|.
   LocalCardMigrationStrikeDatabase* GetLocalCardMigrationStrikeDatabase();
 
+  // Filter the |migratable_credit_cards_| with |supported_card_bin_ranges_| and
+  // keep supported local cards in |migratable_credit_cards_|.
+  void FilterOutUnsupportedLocalCards();
+
   // Pops up a larger, modal dialog showing the local cards to be uploaded.
   void ShowMainMigrationDialog();
 
@@ -215,6 +224,10 @@
   std::unique_ptr<LocalCardMigrationStrikeDatabase>
       local_card_migration_strike_database_;
 
+  // List of BIN prefix ranges which are supoorted, with the first and second
+  // number in the pair being the start and end of the range.
+  std::vector<std::pair<int, int>> supported_card_bin_ranges_;
+
   base::WeakPtrFactory<LocalCardMigrationManager> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationManager);
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
index bd35d564..5ded259 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
@@ -1183,4 +1183,126 @@
       1);
 }
 
+// Use one unsupported local card with more unsupported local cards available
+// but DoNotMigrateUnsupportedLocalCards experiment flag is off, will trigger
+// migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_MigrateUnsupportedWhenExpOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillDoNotMigrateUnsupportedLocalCards);
+
+  // Set the billing_customer_number to designate existence of a Payments
+  // account.
+  personal_data_.SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
+
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "1", "guid1");
+  // Add another local credit card.
+  AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "1", "guid2");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Set up the supported card bin ranges so that there are no supported cards.
+  std::vector<std::pair<int, int>> supported_card_bin_ranges{
+      std::make_pair(34, 34), std::make_pair(300, 305)};
+  payments_client_->SetSupportedBINRanges(supported_card_bin_ranges);
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->IntermediatePromptWasShown());
+}
+
+// Use one unsupported local card with more unsupported local cards will not
+// trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_MigrationAbortWhenNoSupportedCards) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillDoNotMigrateUnsupportedLocalCards);
+
+  // Set the billing_customer_number to designate existence of a Payments
+  // account.
+  personal_data_.SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
+
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "1", "guid1");
+  // Add another local credit card.
+  AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "1", "guid2");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Set up the supported card bin ranges so that there are no supported cards.
+  std::vector<std::pair<int, int>> supported_card_bin_ranges{
+      std::make_pair(34, 34), std::make_pair(300, 305)};
+  payments_client_->SetSupportedBINRanges(supported_card_bin_ranges);
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->migratable_credit_cards_.empty());
+  EXPECT_FALSE(local_card_migration_manager_->IntermediatePromptWasShown());
+}
+
+// Use one supported local card with more unsupported local cards available
+// will trigger migration with the only supported local card.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_MigrateWhenHasSupportedLocalCard) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillDoNotMigrateUnsupportedLocalCards);
+
+  // Set the billing_customer_number to designate existence of a Payments
+  // account.
+  personal_data_.SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
+
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "1", "guid1");
+  // Add another local credit card.
+  AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "1", "guid2");
+
+  // Set up the supported card bin ranges so that there is only one supported
+  // card.
+  std::vector<std::pair<int, int>> supported_card_bin_ranges{
+      std::make_pair(411, 412), std::make_pair(300, 305)};
+  payments_client_->SetSupportedBINRanges(supported_card_bin_ranges);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+
+  EXPECT_EQ(static_cast<int>(
+                local_card_migration_manager_->migratable_credit_cards_.size()),
+            1);
+  EXPECT_EQ(local_card_migration_manager_->migratable_credit_cards_[0]
+                .credit_card()
+                .number(),
+            base::ASCIIToUTF16("4111111111111111"));
+  EXPECT_TRUE(local_card_migration_manager_->IntermediatePromptWasShown());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 2266125..c2f1f6f 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1226,26 +1226,25 @@
 
   // Try to update with just the origin changed.
   AutofillProfile original_profile(profile);
+  ASSERT_FALSE(original_profile.IsVerified());
   CreditCard original_credit_card(credit_card);
   profile.set_origin(kSettingsOrigin);
   credit_card.set_origin(kSettingsOrigin);
 
   EXPECT_TRUE(profile.IsVerified());
   EXPECT_TRUE(credit_card.IsVerified());
-
   UpdateProfileOnPersonalDataManager(profile);
   personal_data_->UpdateCreditCard(credit_card);
 
-  // Note: No refresh, as no update is expected.
-
+  // Credit Card origin should not be overwritten.
   const std::vector<AutofillProfile*>& profiles2 =
       personal_data_->GetProfiles();
   const std::vector<CreditCard*>& cards2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, profiles2.size());
   ASSERT_EQ(1U, cards2.size());
-  EXPECT_NE(profile.origin(), profiles2[0]->origin());
+  EXPECT_EQ(profile.origin(), profiles2[0]->origin());
   EXPECT_NE(credit_card.origin(), cards2[0]->origin());
-  EXPECT_EQ(original_profile.origin(), profiles2[0]->origin());
+  EXPECT_NE(original_profile.origin(), profiles2[0]->origin());
   EXPECT_EQ(original_credit_card.origin(), cards2[0]->origin());
 
   // Try to update with data changed as well.
@@ -1268,6 +1267,39 @@
   EXPECT_EQ(credit_card.origin(), cards3[0]->origin());
 }
 
+// Test that updating a verified profile with another profile whose only
+// difference is the origin, would not change the old profile, and thus it would
+// remain verified.
+TEST_F(PersonalDataManagerTest, UpdateVerifiedProfilesOrigin) {
+  // Start with verified data.
+  AutofillProfile profile(base::GenerateGUID(), kSettingsOrigin);
+  test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison",
+                       "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+                       "Hollywood", "CA", "91601", "US", "12345678910");
+  ASSERT_TRUE(profile.IsVerified());
+  AddProfileToPersonalDataManager(profile);
+
+  const std::vector<AutofillProfile*>& profiles1 =
+      personal_data_->GetProfiles();
+  ASSERT_EQ(1U, profiles1.size());
+  EXPECT_EQ(0, profile.Compare(*profiles1[0]));
+
+  // Try to update with just the origin changed to a non-setting origin.
+  AutofillProfile new_profile(profile);
+  new_profile.set_origin("");
+  ASSERT_FALSE(new_profile.IsVerified());
+
+  UpdateProfileOnPersonalDataManager(profile);
+
+  // Verified profile origin should not be overwritten.
+  const std::vector<AutofillProfile*>& profiles2 =
+      personal_data_->GetProfiles();
+  ASSERT_EQ(1U, profiles2.size());
+  EXPECT_EQ(profile.origin(), profiles2[0]->origin());
+  EXPECT_NE(new_profile.origin(), profiles2[0]->origin());
+  EXPECT_TRUE(profiles2[0]->IsVerified());
+}
+
 // Makes sure that full cards are re-masked when full PAN storage is off.
 TEST_F(PersonalDataManagerTest, RefuseToStoreFullCard) {
 // On Linux this should be disabled automatically. Elsewhere, only if the
diff --git a/components/autofill/core/browser/phone_number.cc b/components/autofill/core/browser/phone_number.cc
index f60a58b..7498365f 100644
--- a/components/autofill/core/browser/phone_number.cc
+++ b/components/autofill/core/browser/phone_number.cc
@@ -31,7 +31,7 @@
 
 }  // namespace
 
-PhoneNumber::PhoneNumber(AutofillProfile* profile) : profile_(profile) {}
+PhoneNumber::PhoneNumber(const AutofillProfile* profile) : profile_(profile) {}
 
 PhoneNumber::PhoneNumber(const PhoneNumber& number) : profile_(nullptr) {
   *this = number;
diff --git a/components/autofill/core/browser/phone_number.h b/components/autofill/core/browser/phone_number.h
index 4773e8c..ab350f3 100644
--- a/components/autofill/core/browser/phone_number.h
+++ b/components/autofill/core/browser/phone_number.h
@@ -21,7 +21,7 @@
 // A form group that stores phone number information.
 class PhoneNumber : public FormGroup {
  public:
-  explicit PhoneNumber(AutofillProfile* profile);
+  explicit PhoneNumber(const AutofillProfile* profile);
   PhoneNumber(const PhoneNumber& number);
   ~PhoneNumber() override;
 
@@ -29,7 +29,7 @@
   bool operator==(const PhoneNumber& other) const;
   bool operator!=(const PhoneNumber& other) const { return !operator==(other); }
 
-  void set_profile(AutofillProfile* profile) { profile_ = profile; }
+  void set_profile(const AutofillProfile* profile) { profile_ = profile; }
 
   // FormGroup implementation:
   void GetMatchingTypes(const base::string16& text,
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index cde9e8db..9c8f937 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -28,6 +28,10 @@
 const base::Feature kAutofillCreditCardAuthentication{
     "AutofillCreditCardAuthentication", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards{
+    "AutofillDoNotMigrateUnsupportedLocalCards",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kAutofillDoNotUploadSaveUnsupportedCards{
     "AutofillDoNotUploadSaveUnsupportedCards",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index 3180375..df49dccc 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -21,6 +21,7 @@
 // All features in alphabetical order.
 extern const base::Feature kAutofillCreditCardAblationExperiment;
 extern const base::Feature kAutofillCreditCardAuthentication;
+extern const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards;
 extern const base::Feature kAutofillDoNotUploadSaveUnsupportedCards;
 extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS;
 extern const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser;
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
index 074118c3..6d6b6d7 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
@@ -17,6 +17,13 @@
   PLATFORM_FAILURE,
 };
 
+struct DmaBufPlane {
+  handle fd_handle;
+  int32 stride;
+  uint32 offset;
+  uint32 size;
+};
+
 // GPU process interface exposed to the browser for encoding JPEG images.
 interface JpegEncodeAccelerator {
   // Initializes the JPEG encoder. Should be called once per encoder
@@ -24,6 +31,8 @@
   // initialization is successful.
   Initialize() => (bool success);
 
+  // TODO(wtlee): To be deprecated. (crbug.com/944705)
+  //
   // Encodes the given buffer that contains one I420 image.
   // |input_fd| and |output_fd| are file descriptors of shared memory.
   // The image is encoded from memory of |input_fd|
@@ -43,4 +52,30 @@
                handle exif_fd, uint32 exif_buffer_size,
                handle output_fd, uint32 output_buffer_size)
       => (int32 buffer_id, uint32 encoded_buffer_size, EncodeStatus status);
+
+  // Encodes the given DMA-buf. The buffer id of output is |buffer_id|. The
+  // size of input image defined in |coded_size_width| and |coded_size_height|
+  // and the format |input_format| represents by its fourcc value. The plane
+  // information of input DMA-buf and output DMA-buf is stored in |input_planes|
+  // and |output_planes| respectively. Although the actual amount of buffers
+  // could be equal to or less than the number of planes, the amount of plane
+  // information |input_planes| and |output_planes| should be same as the number
+  // of planes. |exif_handle| is the shared memory buffer, with
+  // |exif_buffer_size| as size, containing Exif data which will be put onto
+  // APP1 segment in the output JPEG image. When the task ends, it returns
+  // |status| as the status code and |encoded_buffer_size| is the actual size of
+  // the encoded JPEG.
+  //
+  // TODO(crbug.com/957469): Consider passing more unique identifier rather than
+  // |buffer_id| for JpegEocdeAccelerator
+  EncodeWithDmaBuf(
+      int32 buffer_id,
+      uint32 input_format,
+      array<DmaBufPlane> input_planes,
+      array<DmaBufPlane> output_planes,
+      handle exif_handle,
+      uint32 exif_buffer_size,
+      int32 coded_size_width,
+      int32 coded_size_height)
+      => (uint32 encoded_buffer_size, EncodeStatus status);
 };
diff --git a/components/gwp_asan/client/BUILD.gn b/components/gwp_asan/client/BUILD.gn
index 1726f2d..0c5c102 100644
--- a/components/gwp_asan/client/BUILD.gn
+++ b/components/gwp_asan/client/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/allocator.gni")
+
 component("client") {
   output_name = "gwp_asan_client"
   sources = [
@@ -13,14 +15,19 @@
     "guarded_page_allocator_win.cc",
     "gwp_asan.cc",
     "gwp_asan.h",
-    "sampling_allocator_shims.cc",
-    "sampling_allocator_shims.h",
   ]
 
   if (is_posix) {
     sources += [ "guarded_page_allocator_posix.cc" ]
   }
 
+  if (use_allocator_shim) {
+    sources += [
+      "sampling_allocator_shims.cc",
+      "sampling_allocator_shims.h",
+    ]
+  }
+
   defines = [ "GWP_ASAN_IMPLEMENTATION" ]
 
   deps = [
@@ -35,8 +42,12 @@
   testonly = true
   sources = [
     "guarded_page_allocator_unittest.cc",
-    "sampling_allocator_shims_unittest.cc",
   ]
+
+  if (use_allocator_shim) {
+    sources += [ "sampling_allocator_shims_unittest.cc" ]
+  }
+
   deps = [
     ":client",
     "//base/allocator:buildflags",
diff --git a/components/gwp_asan/client/gwp_asan.cc b/components/gwp_asan/client/gwp_asan.cc
index 69084aa..dc04d68 100644
--- a/components/gwp_asan/client/gwp_asan.cc
+++ b/components/gwp_asan/client/gwp_asan.cc
@@ -8,6 +8,7 @@
 #include <cmath>
 #include <limits>
 
+#include "base/allocator/buildflags.h"
 #include "base/debug/crash_logging.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
@@ -18,13 +19,17 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "components/gwp_asan/client/guarded_page_allocator.h"
+
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
 #include "components/gwp_asan/client/sampling_allocator_shims.h"
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 
 namespace gwp_asan {
 
 namespace internal {
 namespace {
 
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
 constexpr int kDefaultMaxAllocations = 35;
 constexpr int kDefaultMaxMetadata = 150;
 
@@ -176,14 +181,19 @@
                         alloc_sampling_freq);
   return true;
 }
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 
 }  // namespace
 }  // namespace internal
 
 void EnableForMalloc(bool is_canary_dev, bool is_browser_process) {
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
   static bool init_once =
       internal::EnableForMalloc(is_canary_dev, is_browser_process);
   ignore_result(init_once);
+#else
+  DLOG(WARNING) << "base::allocator shims are unavailable for GWP-ASan.";
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 }
 
 }  // namespace gwp_asan
diff --git a/components/gwp_asan/client/sampling_allocator_shims.cc b/components/gwp_asan/client/sampling_allocator_shims.cc
index a8d50acb..6a201fa5 100644
--- a/components/gwp_asan/client/sampling_allocator_shims.cc
+++ b/components/gwp_asan/client/sampling_allocator_shims.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/allocator/allocator_shim.h"
-#include "base/allocator/buildflags.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
@@ -326,17 +325,11 @@
                            size_t num_metadata,
                            size_t total_pages,
                            size_t sampling_frequency) {
-#if BUILDFLAG(USE_ALLOCATOR_SHIM)
   gpa = new GuardedPageAllocator();
   gpa->Init(max_allocated_pages, num_metadata, total_pages);
   RegisterAllocatorAddress(gpa->GetCrashKeyAddress());
   sampling_state.Init(sampling_frequency);
   base::allocator::InsertAllocatorDispatch(&g_allocator_dispatch);
-#else
-  ignore_result(g_allocator_dispatch);
-  ignore_result(gpa);
-  DLOG(WARNING) << "base::allocator shims are unavailable for GWP-ASan.";
-#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 }
 
 }  // namespace internal
diff --git a/components/gwp_asan/client/sampling_allocator_shims_unittest.cc b/components/gwp_asan/client/sampling_allocator_shims_unittest.cc
index f287fe6..8892d85b 100644
--- a/components/gwp_asan/client/sampling_allocator_shims_unittest.cc
+++ b/components/gwp_asan/client/sampling_allocator_shims_unittest.cc
@@ -10,7 +10,6 @@
 #include <string>
 
 #include "base/allocator/allocator_shim.h"
-#include "base/allocator/buildflags.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gtest_util.h"
@@ -26,8 +25,6 @@
 // These tests install global allocator shims so they are not safe to run in
 // multi-threaded contexts. Instead they're implemented as multi-process tests.
 
-#if BUILDFLAG(USE_ALLOCATOR_SHIM)
-
 #if defined(OS_WIN)
 #include <malloc.h>
 static size_t GetAllocatedSize(void *mem) {
@@ -328,5 +325,3 @@
 
 }  // namespace internal
 }  // namespace gwp_asan
-
-#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
diff --git a/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java b/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java
index d1c2d937..71b25c5 100644
--- a/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java
+++ b/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItem.java
@@ -109,7 +109,7 @@
 
     public OfflineItem() {
         id = new ContentId();
-        filter = OfflineItemFilter.FILTER_OTHER;
+        filter = OfflineItemFilter.OTHER;
         state = OfflineItemState.COMPLETE;
     }
 
diff --git a/components/offline_items_collection/core/offline_item_filter.h b/components/offline_items_collection/core/offline_item_filter.h
index 26c91b5..b773e17 100644
--- a/components/offline_items_collection/core/offline_item_filter.h
+++ b/components/offline_items_collection/core/offline_item_filter.h
@@ -11,6 +11,7 @@
 
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offline_items_collection
+// GENERATED_JAVA_PREFIX_TO_STRIP: FILTER_
 enum OfflineItemFilter {
   FILTER_PAGE = 0,
   FILTER_VIDEO,
@@ -19,8 +20,7 @@
   FILTER_DOCUMENT,
   FILTER_OTHER,
 
-  // Maximum value.
-  FILTER_BOUNDARY,
+  FILTER_NUM_ENTRIES,
 };
 
 // Implemented for test-only. See test_support/offline_item_test_support.cc.
diff --git a/components/offline_items_collection/core/offline_item_state.h b/components/offline_items_collection/core/offline_item_state.h
index 780da574..b18bf60 100644
--- a/components/offline_items_collection/core/offline_item_state.h
+++ b/components/offline_items_collection/core/offline_item_state.h
@@ -20,7 +20,7 @@
   FAILED,
   PAUSED,  // TODO(dtrainor): Make sure exposing a PAUSED state does not impact
            // downloads resumption.
-  MAX_DOWNLOAD_STATE,
+  NUM_ENTRIES,
 };
 
 // Implemented for testing only. See test_support/offline_item_test_support.cc.
diff --git a/components/offline_items_collection/core/test_support/offline_item_test_support.cc b/components/offline_items_collection/core/test_support/offline_item_test_support.cc
index a6f5646..e1989d7 100644
--- a/components/offline_items_collection/core/test_support/offline_item_test_support.cc
+++ b/components/offline_items_collection/core/test_support/offline_item_test_support.cc
@@ -66,8 +66,8 @@
       return os << "FAILED";
     case PAUSED:
       return os << "PAUSED";
-    case MAX_DOWNLOAD_STATE:
-      return os << "MAX_DOWNLOAD_STATE";
+    case NUM_ENTRIES:
+      return os << "NUM_ENTRIES";
   }
   CHECK(false) << "state=" << static_cast<int>(state);
   return os;
@@ -171,8 +171,8 @@
       return os << "FILTER_DOCUMENT";
     case FILTER_OTHER:
       return os << "FILTER_OTHER";
-    case FILTER_BOUNDARY:
-      return os << "FILTER_BOUNDARY";
+    case FILTER_NUM_ENTRIES:
+      return os << "FILTER_NUM_ENTRIES";
   }
   CHECK(false) << "state=" << static_cast<int>(state);
   return os;
diff --git a/components/resources/default_100_percent/autofill/googlepay.png b/components/resources/default_100_percent/autofill/googlepay.png
deleted file mode 100644
index 51e11331..0000000
--- a/components/resources/default_100_percent/autofill/googlepay.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/autofill/googlepay_dark.png b/components/resources/default_100_percent/autofill/googlepay_dark.png
deleted file mode 100644
index 0299246..0000000
--- a/components/resources/default_100_percent/autofill/googlepay_dark.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png
deleted file mode 100644
index c2fd403..0000000
--- a/components/resources/default_100_percent/autofill/infobar_autofill_googlepay_with_divider.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/autofill/migration_header.png b/components/resources/default_100_percent/autofill/migration_header.png
deleted file mode 100644
index f72197d7..0000000
--- a/components/resources/default_100_percent/autofill/migration_header.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/autofill/migration_header_dark.png b/components/resources/default_100_percent/autofill/migration_header_dark.png
deleted file mode 100644
index 5893ea3..0000000
--- a/components/resources/default_100_percent/autofill/migration_header_dark.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/autofill/googlepay.png b/components/resources/default_200_percent/autofill/googlepay.png
deleted file mode 100644
index 5cb11e8..0000000
--- a/components/resources/default_200_percent/autofill/googlepay.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/autofill/googlepay_dark.png b/components/resources/default_200_percent/autofill/googlepay_dark.png
deleted file mode 100644
index 45b0e25..0000000
--- a/components/resources/default_200_percent/autofill/googlepay_dark.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png
deleted file mode 100644
index 23b408d..0000000
--- a/components/resources/default_200_percent/autofill/infobar_autofill_googlepay_with_divider.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/autofill/migration_header.png b/components/resources/default_200_percent/autofill/migration_header.png
deleted file mode 100644
index 1749cb5..0000000
--- a/components/resources/default_200_percent/autofill/migration_header.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/autofill/migration_header_dark.png b/components/resources/default_200_percent/autofill/migration_header_dark.png
deleted file mode 100644
index b39066c..0000000
--- a/components/resources/default_200_percent/autofill/migration_header_dark.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/autofill/googlepay.png b/components/resources/default_300_percent/autofill/googlepay.png
deleted file mode 100644
index 4c04b3f..0000000
--- a/components/resources/default_300_percent/autofill/googlepay.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/autofill/googlepay_dark.png b/components/resources/default_300_percent/autofill/googlepay_dark.png
deleted file mode 100644
index 18bbe69a..0000000
--- a/components/resources/default_300_percent/autofill/googlepay_dark.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png b/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png
deleted file mode 100644
index 76afa20..0000000
--- a/components/resources/default_300_percent/autofill/infobar_autofill_googlepay_with_divider.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/autofill/migration_header.png b/components/resources/default_300_percent/autofill/migration_header.png
deleted file mode 100644
index ca1d6fa..0000000
--- a/components/resources/default_300_percent/autofill/migration_header.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/autofill/migration_header_dark.png b/components/resources/default_300_percent/autofill/migration_header_dark.png
deleted file mode 100644
index 37992e47b..0000000
--- a/components/resources/default_300_percent/autofill/migration_header_dark.png
+++ /dev/null
Binary files differ
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index dfeacca..a000b88 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -141,23 +141,20 @@
   std::move(callback).Run(result);
 }
 
-void ClipboardHostImpl::WriteText(ui::ClipboardType,
-                                  const base::string16& text) {
+void ClipboardHostImpl::WriteText(const base::string16& text) {
   clipboard_writer_->WriteText(text);
 }
 
-void ClipboardHostImpl::WriteHtml(ui::ClipboardType,
-                                  const base::string16& markup,
+void ClipboardHostImpl::WriteHtml(const base::string16& markup,
                                   const GURL& url) {
   clipboard_writer_->WriteHTML(markup, url.spec());
 }
 
-void ClipboardHostImpl::WriteSmartPasteMarker(ui::ClipboardType) {
+void ClipboardHostImpl::WriteSmartPasteMarker() {
   clipboard_writer_->WriteWebSmartPaste();
 }
 
 void ClipboardHostImpl::WriteCustomData(
-    ui::ClipboardType,
     const base::flat_map<base::string16, base::string16>& data) {
   base::Pickle pickle;
   ui::WriteCustomDataToPickle(data, &pickle);
@@ -165,17 +162,16 @@
       pickle, ui::ClipboardFormatType::GetWebCustomDataType());
 }
 
-void ClipboardHostImpl::WriteBookmark(ui::ClipboardType,
-                                      const std::string& url,
+void ClipboardHostImpl::WriteBookmark(const std::string& url,
                                       const base::string16& title) {
   clipboard_writer_->WriteBookmark(title, url);
 }
 
-void ClipboardHostImpl::WriteImage(ui::ClipboardType, const SkBitmap& bitmap) {
+void ClipboardHostImpl::WriteImage(const SkBitmap& bitmap) {
   clipboard_writer_->WriteImage(bitmap);
 }
 
-void ClipboardHostImpl::CommitWrite(ui::ClipboardType) {
+void ClipboardHostImpl::CommitWrite() {
   clipboard_writer_.reset(
       new ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE));
 }
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h
index 82a0d6b..dd64dec7 100644
--- a/content/browser/renderer_host/clipboard_host_impl.h
+++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -55,21 +55,15 @@
   void ReadCustomData(ui::ClipboardType clipboard_type,
                       const base::string16& type,
                       ReadCustomDataCallback callback) override;
-  void WriteText(ui::ClipboardType clipboard_type,
-                 const base::string16& text) override;
-  void WriteHtml(ui::ClipboardType clipboard_type,
-                 const base::string16& markup,
-                 const GURL& url) override;
-  void WriteSmartPasteMarker(ui::ClipboardType clipboard_type) override;
+  void WriteText(const base::string16& text) override;
+  void WriteHtml(const base::string16& markup, const GURL& url) override;
+  void WriteSmartPasteMarker() override;
   void WriteCustomData(
-      ui::ClipboardType clipboard_type,
       const base::flat_map<base::string16, base::string16>& data) override;
-  void WriteBookmark(ui::ClipboardType clipboard_type,
-                     const std::string& url,
+  void WriteBookmark(const std::string& url,
                      const base::string16& title) override;
-  void WriteImage(ui::ClipboardType clipboard_type,
-                  const SkBitmap& bitmap) override;
-  void CommitWrite(ui::ClipboardType clipboard_type) override;
+  void WriteImage(const SkBitmap& bitmap) override;
+  void CommitWrite() override;
 #if defined(OS_MACOSX)
   void WriteStringToFindPboard(const base::string16& text) override;
 #endif
diff --git a/content/browser/renderer_host/clipboard_host_impl_unittest.cc b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
index 05cb4a12..62ee6894 100644
--- a/content/browser/renderer_host/clipboard_host_impl_unittest.cc
+++ b/content/browser/renderer_host/clipboard_host_impl_unittest.cc
@@ -47,10 +47,10 @@
   SkBitmap bitmap;
   bitmap.allocN32Pixels(3, 2);
   bitmap.eraseARGB(255, 0, 255, 0);
-  mojo_clipboard()->WriteImage(ui::CLIPBOARD_TYPE_COPY_PASTE, bitmap);
+  mojo_clipboard()->WriteImage(bitmap);
   uint64_t sequence_number =
       system_clipboard()->GetSequenceNumber(ui::CLIPBOARD_TYPE_COPY_PASTE);
-  mojo_clipboard()->CommitWrite(ui::CLIPBOARD_TYPE_COPY_PASTE);
+  mojo_clipboard()->CommitWrite();
   base::RunLoop().RunUntilIdle();
 
   EXPECT_NE(sequence_number, system_clipboard()->GetSequenceNumber(
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc
index 8ce7bdc..f5d60db 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.cc
+++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -330,9 +330,9 @@
       markup = instance_->GetSelectedText(true);
       text = instance_->GetSelectedText(false);
     }
-    clipboard_->WriteHtml(ui::CLIPBOARD_TYPE_COPY_PASTE, markup, GURL());
-    clipboard_->WriteText(ui::CLIPBOARD_TYPE_COPY_PASTE, text);
-    clipboard_->CommitWrite(ui::CLIPBOARD_TYPE_COPY_PASTE);
+    clipboard_->WriteHtml(markup, GURL());
+    clipboard_->WriteText(text);
+    clipboard_->CommitWrite();
 
     instance_->ReplaceSelection("");
     return true;
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 10a0dbb..ba909aa1 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -946,18 +946,16 @@
   if (is_win) {
     data_deps += [ "//build/win:copy_cdb_to_output" ]
   }
-  if (is_posix && !is_android) {
+  if (is_posix) {
     data_deps += [
-      "//third_party/breakpad:dump_syms($host_toolchain)",
-      "//third_party/breakpad:minidump_stackwalk($host_toolchain)",
+      "//third_party/breakpad:dump_syms",
+      "//third_party/breakpad:minidump_stackwalk",
     ]
   }
   if (is_android) {
     data_deps += [
-      "//third_party/breakpad:dump_syms",
       "//third_party/breakpad:microdump_stackwalk",
       "//third_party/breakpad:minidump_dump",
-      "//third_party/breakpad:minidump_stackwalk",
       "//third_party/breakpad:symupload",
       "//tools/android/forwarder2",
     ]
diff --git a/content/shell/tools/breakpad_integration_test.py b/content/shell/tools/breakpad_integration_test.py
index 8822edc..478d545 100755
--- a/content/shell/tools/breakpad_integration_test.py
+++ b/content/shell/tools/breakpad_integration_test.py
@@ -238,12 +238,7 @@
         failure = 'Failed to run generate_breakpad_symbols.py.'
         subprocess.check_call(cmd)
 
-    print '# Running test without trap handler.'
     run_test(options, crash_dir, symbols_dir, platform)
-    print '# Running test with trap handler.'
-    run_test(options, crash_dir, symbols_dir, platform,
-             additional_arguments =
-               ['--enable-features=WebAssemblyTrapHandler'])
 
   except:
     print 'FAIL: %s' % failure
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
index 8cf984b..6c65cc8 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -11,7 +11,10 @@
 import sys
 import run_gpu_integration_test
 import gpu_project_config
+import inspect
+import itertools
 
+from collections import defaultdict
 
 from telemetry.testing import browser_test_runner
 from telemetry.internal.platform import system_info
@@ -19,8 +22,13 @@
 from gpu_tests import context_lost_integration_test
 from gpu_tests import gpu_integration_test
 from gpu_tests import path_util
+from gpu_tests import pixel_integration_test
+from gpu_tests import pixel_test_pages
 from gpu_tests import webgl_conformance_integration_test
 
+from py_utils import discover
+from typ import expectations_parser
+
 path_util.AddDirToPathIfNeeded(path_util.GetChromiumSrcDir(), 'tools', 'perf')
 from chrome_telemetry_build import chromium_config
 
@@ -86,7 +94,7 @@
 
   @classmethod
   def GenerateGpuTests(cls, options):
-    pass
+    return []
 
   def RunActualGpuTest(self, test_path, *args):
     pass
@@ -133,6 +141,90 @@
   return set(MockTestCase.GenerateTags(args, possible_browser))
 
 
+def _checkTestExpectationsAreForExistingTests(
+    test_class, mock_options, test_names=None):
+  expectations_file = test_class.ExpectationsFiles()[0]
+  test_names = test_names or [
+      args[0] for args in
+      test_class.GenerateGpuTests(mock_options)]
+  trie = {}
+  for test in test_names:
+    _trie = trie.setdefault(test[0], {})
+    for l in test[1:]:
+      _trie = _trie.setdefault(l, {})
+  f = open(expectations_file, 'r')
+  expectations_file = os.path.basename(expectations_file)
+  expectations = f.read()
+  f.close()
+  parser = expectations_parser.TaggedTestListParser(expectations)
+  for exp in parser.expectations:
+    _trie = trie
+    for i, l in enumerate(exp.test):
+      if l == '*':
+        assert i == len(exp.test)-1, (
+            ("%s:%d: '*' can only be at the "
+            "end of a test expectation's patttern")
+            % (expectations_file, exp.lineno))
+        break
+      assert l in _trie, (
+        "%s:%d: Glob '%s' does not match with any tests in the %s test suite" %
+        (expectations_file, exp.lineno, exp.test, test_class.Name()))
+      _trie = _trie[l]
+
+
+def _checkTestExpectationsForCollision(expectations, file_name):
+  parser = expectations_parser.TaggedTestListParser(expectations)
+  tests_to_exps = defaultdict(list)
+  conflicts_found = False
+  for exp in parser.expectations:
+    if not exp.test.endswith('*'):
+      tests_to_exps[exp.test].append(exp)
+  error_msg = ''
+  for pattern, exps in tests_to_exps.items():
+    if len(exps) > 1:
+      error_msg += ('\n\nFound conflicts for test %s in %s:\n' %
+                    (pattern, file_name))
+    for e1, e2 in itertools.combinations(exps, 2):
+      if e1.tags.issubset(e2.tags) or e2.tags.issubset(e1.tags):
+        conflicts_found = True
+        error_msg += ('  line %d conflicts with line %d\n' %
+                      (e1.lineno, e2.lineno))
+  assert not conflicts_found, error_msg
+
+
+def _testCheckTestExpectationsAreForExistingTests(expectations):
+  options = MockArgs()
+  expectations_file = tempfile.NamedTemporaryFile(delete=False)
+  expectations_file.write(expectations)
+  expectations_file.close()
+
+  class _MockTestCase(object):
+    @classmethod
+    def Name(cls):
+      return '_MockTestCase'
+
+    @classmethod
+    def GenerateGpuTests(cls, options):
+      tests = [('a/b/c', ())]
+      for t in tests:
+        yield t
+
+    @classmethod
+    def ExpectationsFiles(cls):
+      return [expectations_file.name]
+
+  _checkTestExpectationsAreForExistingTests(_MockTestCase, options)
+
+def _FindTestCases():
+  test_cases = []
+  for start_dir in gpu_project_config.CONFIG.start_dirs:
+    modules_to_classes = discover.DiscoverClasses(
+        start_dir,
+        gpu_project_config.CONFIG.top_level_dir,
+        base_class=gpu_integration_test.GpuIntegrationTest)
+    test_cases.extend(modules_to_classes.values())
+  return test_cases
+
 class GpuIntegrationTestUnittest(unittest.TestCase):
   def setUp(self):
     self._test_state = {}
@@ -158,6 +250,86 @@
     finally:
       temp_file.close()
 
+  def testCollisionInTestExpectationCausesAssertion(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win ] a/b/c/d [ Failure ]
+    [ intel win debug ] a/b/c/d [ Skip ]
+    [ intel  ] a/b/c/d [ Failure ]
+    [ amd mac ] a/b [ RetryOnFailure ]
+    [ mac ] a/b [ Skip ]
+    '''
+    with self.assertRaises(AssertionError) as context:
+      _checkTestExpectationsForCollision(test_expectations, 'test.txt')
+    self.assertIn("Found conflicts for test a/b/c/d in test.txt:",
+      str(context.exception))
+    self.assertIn('line 4 conflicts with line 5',
+      str(context.exception))
+    self.assertIn('line 4 conflicts with line 6',
+      str(context.exception))
+    self.assertIn('line 5 conflicts with line 6',
+      str(context.exception))
+    self.assertIn("Found conflicts for test a/b in test.txt:",
+      str(context.exception))
+    self.assertIn('line 7 conflicts with line 8',
+      str(context.exception))
+
+  def testNoCollisionInTestExpectations(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win release ] a/b/* [ Failure ]
+    [ intel debug ] a/b/c/d [ Failure ]
+    [ nvidia debug ] a/b/c/d [ Failure ]
+    '''
+    _checkTestExpectationsForCollision(test_expectations, 'test.txt')
+
+  def testNoCollisionsInGpuTestExpectations(self):
+    for test_case in _FindTestCases():
+      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
+        if test_case.ExpectationsFiles():
+          with open(test_case.ExpectationsFiles()[0]) as f:
+            _checkTestExpectationsForCollision(f.read(),
+            os.path.basename(f.name))
+
+  def testGpuTestExpectationsAreForExistingTests(self):
+    options = MockArgs()
+    for test_case in _FindTestCases():
+      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
+        if (test_case.Name() not in ('pixel', 'webgl_conformance')
+            and test_case.ExpectationsFiles()):
+          _checkTestExpectationsAreForExistingTests(test_case, options)
+
+  def testPixelTestsExpectationsAreForExistingTests(self):
+    pixel_test_names = []
+    for _, method in inspect.getmembers(
+        pixel_test_pages.PixelTestPages, predicate=inspect.isfunction):
+      pixel_test_names.extend(
+          [p.name for p in method(
+              pixel_integration_test.PixelIntegrationTest.test_base_name)])
+    _checkTestExpectationsAreForExistingTests(
+        pixel_integration_test.PixelIntegrationTest,
+        MockArgs(), pixel_test_names)
+
+  def testStarMustBeAtEndOfTestPattern(self):
+    with self.assertRaises(AssertionError) as context:
+      _testCheckTestExpectationsAreForExistingTests(
+          'a/b*/c [ Failure ]\n')
+    self.assertIn(("1: '*' can only be at the end of"
+                   " a test expectation's patttern"),
+                  str(context.exception))
+
+  def testExpectationPatternNotInGeneratedTests(self):
+    with self.assertRaises(AssertionError) as context:
+      _testCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]')
+    self.assertIn(("1: Glob 'a/b/d' does not match with any"
+                  " tests in the _MockTestCase test suite"),
+                  str(context.exception))
+
+  def testGlobMatchesTestName(self):
+    _testCheckTestExpectationsAreForExistingTests('a/b* [ Failure ]')
+
   def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self):
     self._RunGpuIntegrationTests(
         'run_tests_with_expectations_files', ['--retry-limit=1'])
diff --git a/content/test/gpu/gpu_tests/pixel_integration_test.py b/content/test/gpu/gpu_tests/pixel_integration_test.py
index 0e13984..305c4d1 100644
--- a/content/test/gpu/gpu_tests/pixel_integration_test.py
+++ b/content/test/gpu/gpu_tests/pixel_integration_test.py
@@ -55,6 +55,9 @@
 
 class PixelIntegrationTest(
     cloud_storage_integration_test_base.CloudStorageIntegrationTestBase):
+
+  test_base_name = 'Pixel'
+
   @classmethod
   def Name(cls):
     """The name by which this test is invoked on the command line."""
@@ -113,18 +116,18 @@
   @classmethod
   def GenerateGpuTests(cls, options):
     cls.SetParsedCommandLineOptions(options)
-    name = 'Pixel'
-    pages = pixel_test_pages.DefaultPages(name)
-    pages += pixel_test_pages.GpuRasterizationPages(name)
-    pages += pixel_test_pages.ExperimentalCanvasFeaturesPages(name)
-    # pages += pixel_test_pages.NoGpuProcessPages(name)
+    namespace = pixel_test_pages.PixelTestPages
+    pages = namespace.DefaultPages(cls.test_base_name)
+    pages += namespace.GpuRasterizationPages(cls.test_base_name)
+    pages += namespace.ExperimentalCanvasFeaturesPages(cls.test_base_name)
+    # pages += namespace.NoGpuProcessPages(cls.test_base_name)
     # The following pages should run only on platforms where SwiftShader is
     # enabled. They are skipped on other platforms through test expectations.
-    # pages += pixel_test_pages.SwiftShaderPages(name)
+    # pages += namespace.SwiftShaderPages(cls.test_base_name)
     if sys.platform.startswith('darwin'):
-      pages += pixel_test_pages.MacSpecificPages(name)
+      pages += namespace.MacSpecificPages(cls.test_base_name)
     if sys.platform.startswith('win'):
-      pages += pixel_test_pages.DirectCompositionPages(name)
+      pages += namespace.DirectCompositionPages(cls.test_base_name)
     for p in pages:
       yield(p.name, gpu_relative_path + p.url, (p))
 
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 5d74401..d65dc63 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -122,1443 +122,1452 @@
   ]
 }
 
+class PixelTestPages(object):
 
-def DefaultPages(base_name):
-  sw_compositing_args = ['--disable-gpu-compositing']
+  @staticmethod
+  def DefaultPages(base_name):
+    sw_compositing_args = ['--disable-gpu-compositing']
 
-  tolerance = 3
-  tolerance_vp9 = 5 # VP9 video requires larger tolerance
-  if sys.platform == 'darwin':
-    # On MacOSX, pixels are slightly off.
-    # https://crbug.com/911895
-    tolerance = 10
-    tolerance_vp9 = 20
+    tolerance = 3
+    tolerance_vp9 = 5 # VP9 video requires larger tolerance
+    if sys.platform == 'darwin':
+      # On MacOSX, pixels are slightly off.
+      # https://crbug.com/911895
+      tolerance = 10
+      tolerance_vp9 = 20
 
-  return [
-    PixelTestPage(
-      'pixel_background_image.html',
-      base_name + '_BackgroundImage',
-      test_rect=[20, 20, 370, 370],
-      revision=3),
+    return [
+      PixelTestPage(
+        'pixel_background_image.html',
+        base_name + '_BackgroundImage',
+        test_rect=[20, 20, 370, 370],
+        revision=3),
 
-    PixelTestPage(
-      'pixel_canvas2d.html',
-      base_name + '_Canvas2DRedBox',
-      test_rect=[0, 0, 300, 300],
-      revision=13),
+      PixelTestPage(
+        'pixel_canvas2d.html',
+        base_name + '_Canvas2DRedBox',
+        test_rect=[0, 0, 300, 300],
+        revision=13),
 
-    PixelTestPage(
-      'pixel_canvas2d_untagged.html',
-      base_name + '_Canvas2DUntagged',
-      test_rect=[0, 0, 257, 257],
-      revision=0),
+      PixelTestPage(
+        'pixel_canvas2d_untagged.html',
+        base_name + '_Canvas2DUntagged',
+        test_rect=[0, 0, 257, 257],
+        revision=0),
 
-    PixelTestPage(
-      'pixel_css3d.html',
-      base_name + '_CSS3DBlueBox',
-      test_rect=[0, 0, 300, 300],
-      revision=24),
+      PixelTestPage(
+        'pixel_css3d.html',
+        base_name + '_CSS3DBlueBox',
+        test_rect=[0, 0, 300, 300],
+        revision=24),
 
-    PixelTestPage(
-      'pixel_webgl_aa_alpha.html',
-      base_name + '_WebGLGreenTriangle_AA_Alpha',
-      test_rect=[0, 0, 300, 300],
-      revision=9),
+      PixelTestPage(
+        'pixel_webgl_aa_alpha.html',
+        base_name + '_WebGLGreenTriangle_AA_Alpha',
+        test_rect=[0, 0, 300, 300],
+        revision=9),
 
-    PixelTestPage(
-      'pixel_webgl_noaa_alpha.html',
-      base_name + '_WebGLGreenTriangle_NoAA_Alpha',
-      test_rect=[0, 0, 300, 300],
-      revision=6),
+      PixelTestPage(
+        'pixel_webgl_noaa_alpha.html',
+        base_name + '_WebGLGreenTriangle_NoAA_Alpha',
+        test_rect=[0, 0, 300, 300],
+        revision=6),
 
-    PixelTestPage(
-      'pixel_webgl_aa_noalpha.html',
-      base_name + '_WebGLGreenTriangle_AA_NoAlpha',
-      test_rect=[0, 0, 300, 300],
-      revision=10),
+      PixelTestPage(
+        'pixel_webgl_aa_noalpha.html',
+        base_name + '_WebGLGreenTriangle_AA_NoAlpha',
+        test_rect=[0, 0, 300, 300],
+        revision=10),
 
-    PixelTestPage(
-      'pixel_webgl_noaa_noalpha.html',
-      base_name + '_WebGLGreenTriangle_NoAA_NoAlpha',
-      test_rect=[0, 0, 300, 300],
-      revision=6),
+      PixelTestPage(
+        'pixel_webgl_noaa_noalpha.html',
+        base_name + '_WebGLGreenTriangle_NoAA_NoAlpha',
+        test_rect=[0, 0, 300, 300],
+        revision=6),
 
-    PixelTestPage(
-      'pixel_webgl_noalpha_implicit_clear.html',
-      base_name + '_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear',
-      test_rect=[0, 0, 300, 300],
-      revision=6),
+      PixelTestPage(
+        'pixel_webgl_noalpha_implicit_clear.html',
+        base_name + '_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear',
+        test_rect=[0, 0, 300, 300],
+        revision=6),
 
-    PixelTestPage(
-      'pixel_webgl_sad_canvas.html',
-      base_name + '_WebGLSadCanvas',
-      test_rect=[0, 0, 300, 300],
-      revision=1,
-      optional_action='CrashGpuProcess'),
+      PixelTestPage(
+        'pixel_webgl_sad_canvas.html',
+        base_name + '_WebGLSadCanvas',
+        test_rect=[0, 0, 300, 300],
+        revision=1,
+        optional_action='CrashGpuProcess'),
 
-    PixelTestPage(
-      'pixel_scissor.html',
-      base_name + '_ScissorTestWithPreserveDrawingBuffer',
-      test_rect=[0, 0, 300, 300],
-      revision=0, # Golden image revision is not used
-      tolerance=3,
-      expected_colors=[
-        {
-          'comment': 'red top',
-          'location': [1, 1],
-          'size': [198, 188],
-          'color': [255, 0, 0],
-        },
-        {
-          'comment': 'green bottom left',
-          'location': [1, 191],
-          'size': [8, 8],
-          'color': [0, 255, 0],
-        },
-        {
-          'comment': 'red bottom right',
-          'location': [11, 191],
-          'size': [188, 8],
-          'color': [255, 0, 0],
-        }
-      ]),
+      PixelTestPage(
+        'pixel_scissor.html',
+        base_name + '_ScissorTestWithPreserveDrawingBuffer',
+        test_rect=[0, 0, 300, 300],
+        revision=0, # Golden image revision is not used
+        tolerance=3,
+        expected_colors=[
+          {
+            'comment': 'red top',
+            'location': [1, 1],
+            'size': [198, 188],
+            'color': [255, 0, 0],
+          },
+          {
+            'comment': 'green bottom left',
+            'location': [1, 191],
+            'size': [8, 8],
+            'color': [0, 255, 0],
+          },
+          {
+            'comment': 'red bottom right',
+            'location': [11, 191],
+            'size': [188, 8],
+            'color': [255, 0, 0],
+          }
+        ]),
 
-    PixelTestPage(
-      'pixel_canvas2d_webgl.html',
-      base_name + '_2DCanvasWebGL',
-      test_rect=[0, 0, 300, 300],
-      revision=13),
+      PixelTestPage(
+        'pixel_canvas2d_webgl.html',
+        base_name + '_2DCanvasWebGL',
+        test_rect=[0, 0, 300, 300],
+        revision=13),
 
-    PixelTestPage(
-      'pixel_background.html',
-      base_name + '_SolidColorBackground',
-      test_rect=[500, 500, 100, 100],
-      revision=1),
+      PixelTestPage(
+        'pixel_background.html',
+        base_name + '_SolidColorBackground',
+        test_rect=[500, 500, 100, 100],
+        revision=1),
 
-    PixelTestPage(
-      'pixel_video_mp4.html',
-      base_name + '_Video_MP4',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      PixelTestPage(
+        'pixel_video_mp4.html',
+        base_name + '_Video_MP4',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    PixelTestPage(
-      'pixel_video_mp4.html',
-      base_name + '_Video_MP4_DXVA',
-      browser_args=['--disable-features=D3D11VideoDecoder'],
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      PixelTestPage(
+        'pixel_video_mp4.html',
+        base_name + '_Video_MP4_DXVA',
+        browser_args=['--disable-features=D3D11VideoDecoder'],
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_aspect_4x3.html',
-      base_name + '_Video_MP4_FourColors_Aspect_4x3',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [28, 133],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [211, 1],
-          'size': [28, 133],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, yellow',
-          'location': [35, 5],
-          'size': [80, 57],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'top right video, red',
-          'location': [125, 5],
-          'size': [80, 57],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom left video, blue',
-          'location': [35, 73],
-          'size': [80, 57],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom right video, green',
-          'location': [125, 73],
-          'size': [80, 57],
-          'color': [44, 255, 16],
-        }
-      ]),
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_aspect_4x3.html',
+        base_name + '_Video_MP4_FourColors_Aspect_4x3',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [28, 133],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [211, 1],
+            'size': [28, 133],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, yellow',
+            'location': [35, 5],
+            'size': [80, 57],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'top right video, red',
+            'location': [125, 5],
+            'size': [80, 57],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom left video, blue',
+            'location': [35, 73],
+            'size': [80, 57],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom right video, green',
+            'location': [125, 73],
+            'size': [80, 57],
+            'color': [44, 255, 16],
+          }
+        ]),
 
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_90.html',
-      base_name + '_Video_MP4_FourColors_Rot_90',
-      test_rect=[0, 0, 427, 240],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [282, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, red',
-          'location': [152, 5],
-          'size': [55, 110],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'top right video, green',
-          'location': [220, 5],
-          'size': [55, 110],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'bottom left video, yellow',
-          'location': [152, 125],
-          'size': [55, 110],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'bottom right video, blue',
-          'location': [220, 125],
-          'size': [55, 110],
-          'color': [12, 12, 255],
-        }
-      ]),
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_90.html',
+        base_name + '_Video_MP4_FourColors_Rot_90',
+        test_rect=[0, 0, 427, 240],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [282, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, red',
+            'location': [152, 5],
+            'size': [55, 110],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'top right video, green',
+            'location': [220, 5],
+            'size': [55, 110],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'bottom left video, yellow',
+            'location': [152, 125],
+            'size': [55, 110],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'bottom right video, blue',
+            'location': [220, 125],
+            'size': [55, 110],
+            'color': [12, 12, 255],
+          }
+        ]),
 
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_180.html',
-      base_name + '_Video_MP4_FourColors_Rot_180',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=[
-        {
-          'comment': 'top left video, green',
-          'location': [5, 5],
-          'size': [110, 57],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'top right video, blue',
-          'location': [125, 5],
-          'size': [110, 57],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom left video, red',
-          'location': [5, 72],
-          'size': [110, 57],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom right video, yellow',
-          'location': [125, 72],
-          'size': [110, 57],
-          'color': [255, 255, 15],
-        }
-      ]),
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_180.html',
+        base_name + '_Video_MP4_FourColors_Rot_180',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=[
+          {
+            'comment': 'top left video, green',
+            'location': [5, 5],
+            'size': [110, 57],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'top right video, blue',
+            'location': [125, 5],
+            'size': [110, 57],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom left video, red',
+            'location': [5, 72],
+            'size': [110, 57],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom right video, yellow',
+            'location': [125, 72],
+            'size': [110, 57],
+            'color': [255, 255, 15],
+          }
+        ]),
 
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_270.html',
-      base_name + '_Video_MP4_FourColors_Rot_270',
-      test_rect=[0, 0, 427, 240],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [282, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, blue',
-          'location': [152, 5],
-          'size': [55, 110],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'top right video, yellow',
-          'location': [220, 5],
-          'size': [55, 110],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'bottom left video, green',
-          'location': [152, 125],
-          'size': [55, 110],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'bottom right video, red',
-          'location': [220, 125],
-          'size': [55, 110],
-          'color': [255, 17, 24],
-        }
-      ]),
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_270.html',
+        base_name + '_Video_MP4_FourColors_Rot_270',
+        test_rect=[0, 0, 427, 240],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [282, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, blue',
+            'location': [152, 5],
+            'size': [55, 110],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'top right video, yellow',
+            'location': [220, 5],
+            'size': [55, 110],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'bottom left video, green',
+            'location': [152, 125],
+            'size': [55, 110],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'bottom right video, red',
+            'location': [220, 125],
+            'size': [55, 110],
+            'color': [255, 17, 24],
+          }
+        ]),
 
-    PixelTestPage(
-      'pixel_video_vp9.html',
-      base_name + '_Video_VP9',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance_vp9,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      PixelTestPage(
+        'pixel_video_vp9.html',
+        base_name + '_Video_VP9',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance_vp9,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    PixelTestPage(
-      'pixel_video_vp9.html',
-      base_name + '_Video_VP9_DXVA',
-      browser_args=['--disable-features=D3D11VideoDecoder'],
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance_vp9,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      PixelTestPage(
+        'pixel_video_vp9.html',
+        base_name + '_Video_VP9_DXVA',
+        browser_args=['--disable-features=D3D11VideoDecoder'],
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance_vp9,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    # The MP4 contains H.264 which is primarily hardware decoded on bots.
-    PixelTestPage(
-      'pixel_video_context_loss.html?src=/media/test/data/four-colors.mp4',
-      base_name + '_Video_Context_Loss_MP4',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      # The MP4 contains H.264 which is primarily hardware decoded on bots.
+      PixelTestPage(
+        'pixel_video_context_loss.html?src=/media/test/data/four-colors.mp4',
+        base_name + '_Video_Context_Loss_MP4',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    # The VP9 test clip is primarily software decoded on bots.
-    PixelTestPage(
-      'pixel_video_context_loss.html?src=/media/test/data/four-colors-vp9.webm',
-      base_name + '_Video_Context_Loss_VP9',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance_vp9,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+      # The VP9 test clip is primarily software decoded on bots.
+      PixelTestPage(
+        ('pixel_video_context_loss.html'
+         '?src=/media/test/data/four-colors-vp9.webm'),
+        base_name + '_Video_Context_Loss_VP9',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance_vp9,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
 
-    PixelTestPage(
-      'pixel_webgl_premultiplied_alpha_false.html',
-      base_name + '_WebGL_PremultipliedAlpha_False',
-      test_rect=[0, 0, 150, 150],
-      revision=0, # Golden image revision is not used
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'brown',
-          'location': [1, 1],
-          'size': [148, 148],
-          # This is the color on an NVIDIA based MacBook Pro if the
-          # sRGB profile's applied correctly.
-          'color': [102, 77, 0],
-          # This is the color if it isn't.
-          # 'color': [101, 76, 12],
-        },
-      ]),
+      PixelTestPage(
+        'pixel_webgl_premultiplied_alpha_false.html',
+        base_name + '_WebGL_PremultipliedAlpha_False',
+        test_rect=[0, 0, 150, 150],
+        revision=0, # Golden image revision is not used
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'brown',
+            'location': [1, 1],
+            'size': [148, 148],
+            # This is the color on an NVIDIA based MacBook Pro if the
+            # sRGB profile's applied correctly.
+            'color': [102, 77, 0],
+            # This is the color if it isn't.
+            # 'color': [101, 76, 12],
+          },
+        ]),
 
-    PixelTestPage(
-      'pixel_webgl2_blitframebuffer_result_displayed.html',
-      base_name + '_WebGL2_BlitFramebuffer_Result_Displayed',
-      test_rect=[0, 0, 200, 200],
-      revision=0, # Golden image revision is not used
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          'location': [1, 1],
-          'size': [180, 180],
-          'color': [0, 255, 0],
-        },
-      ]),
+      PixelTestPage(
+        'pixel_webgl2_blitframebuffer_result_displayed.html',
+        base_name + '_WebGL2_BlitFramebuffer_Result_Displayed',
+        test_rect=[0, 0, 200, 200],
+        revision=0, # Golden image revision is not used
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            'location': [1, 1],
+            'size': [180, 180],
+            'color': [0, 255, 0],
+          },
+        ]),
 
-    PixelTestPage(
-      'pixel_webgl2_clearbufferfv_result_displayed.html',
-      base_name + '_WebGL2_ClearBufferfv_Result_Displayed',
-      test_rect=[0, 0, 200, 200],
-      revision=0, # Golden image revision is not used
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          'location': [1, 1],
-          'size': [180, 180],
-          'color': [0, 255, 0],
-        },
-      ]),
+      PixelTestPage(
+        'pixel_webgl2_clearbufferfv_result_displayed.html',
+        base_name + '_WebGL2_ClearBufferfv_Result_Displayed',
+        test_rect=[0, 0, 200, 200],
+        revision=0, # Golden image revision is not used
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            'location': [1, 1],
+            'size': [180, 180],
+            'color': [0, 255, 0],
+          },
+        ]),
 
-    PixelTestPage(
-      'pixel_repeated_webgl_to_2d.html',
-      base_name + '_RepeatedWebGLTo2D',
-      test_rect=[0, 0, 256, 256],
-      revision=0, # Golden image revision is not used
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          # 64x64 rectangle around the center at (128,128)
-          'location': [96, 96],
-          'size': [64, 64],
-          'color': [0, 255, 0],
-        },
-      ]),
+      PixelTestPage(
+        'pixel_repeated_webgl_to_2d.html',
+        base_name + '_RepeatedWebGLTo2D',
+        test_rect=[0, 0, 256, 256],
+        revision=0, # Golden image revision is not used
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            # 64x64 rectangle around the center at (128,128)
+            'location': [96, 96],
+            'size': [64, 64],
+            'color': [0, 255, 0],
+          },
+        ]),
 
-    PixelTestPage(
-      'pixel_repeated_webgl_to_2d.html',
-      base_name + '_RepeatedWebGLTo2D_SoftwareCompositing',
-      test_rect=[0, 0, 256, 256],
-      revision=0, # Golden image revision is not used
-      browser_args=sw_compositing_args,
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          # 64x64 rectangle around the center at (128,128)
-          'location': [96, 96],
-          'size': [64, 64],
-          'color': [0, 255, 0],
-        },
-      ]),
-  ]
-
-
-# Pages that should be run with GPU rasterization enabled.
-def GpuRasterizationPages(base_name):
-  browser_args = ['--force-gpu-rasterization']
-  return [
-    PixelTestPage(
-      'pixel_background.html',
-      base_name + '_GpuRasterization_BlueBox',
-      test_rect=[0, 0, 220, 220],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=0,
-      expected_colors=[
-        {
-          'comment': 'body-t',
-          'location': [5, 5],
-          'size': [1, 1],
-          'color': [0, 128, 0],
-        },
-        {
-          'comment': 'body-r',
-          'location': [215, 5],
-          'size': [1, 1],
-          'color': [0, 128, 0],
-        },
-        {
-          'comment': 'body-b',
-          'location': [215, 215],
-          'size': [1, 1],
-          'color': [0, 128, 0],
-        },
-        {
-          'comment': 'body-l',
-          'location': [5, 215],
-          'size': [1, 1],
-          'color': [0, 128, 0],
-        },
-        {
-          'comment': 'background-t',
-          'location': [30, 30],
-          'size': [1, 1],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'background-r',
-          'location': [170, 30],
-          'size': [1, 1],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'background-b',
-          'location': [170, 170],
-          'size': [1, 1],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'background-l',
-          'location': [30, 170],
-          'size': [1, 1],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'box-t',
-          'location': [70, 70],
-          'size': [1, 1],
-          'color': [0, 0, 255],
-        },
-        {
-          'comment': 'box-r',
-          'location': [140, 70],
-          'size': [1, 1],
-          'color': [0, 0, 255],
-        },
-        {
-          'comment': 'box-b',
-          'location': [140, 140],
-          'size': [1, 1],
-          'color': [0, 0, 255],
-        },
-        {
-          'comment': 'box-l',
-          'location': [70, 140],
-          'size': [1, 1],
-          'color': [0, 0, 255],
-        }
-      ]),
-    PixelTestPage(
-      'concave_paths.html',
-      base_name + '_GpuRasterization_ConcavePaths',
-      test_rect=[0, 0, 100, 100],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=0,
-      expected_colors=[
-        {
-          'comment': 'outside',
-          'location': [80, 60],
-          'size': [1, 1],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside',
-          'location': [28, 20],
-          'size': [1, 1],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'inside',
-          'location': [32, 25],
-          'size': [1, 1],
-          'color': [255, 215, 0],
-        },
-        {
-          'comment': 'inside',
-          'location': [80, 80],
-          'size': [1, 1],
-          'color': [255, 215, 0],
-        }
-      ])
-  ]
-
-# Pages that should be run with experimental canvas features.
-def ExperimentalCanvasFeaturesPages(base_name):
-  browser_args = [
-    '--enable-experimental-web-platform-features'] # for lowLatency
-  unaccelerated_args = [
-    '--disable-accelerated-2d-canvas',
-    '--disable-gpu-compositing']
-
-  return [
-    PixelTestPage(
-      'pixel_offscreenCanvas_transfer_after_style_resize.html',
-      base_name + '_OffscreenCanvasTransferAfterStyleResize',
-      test_rect=[0, 0, 350, 350],
-      revision=10,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_transfer_before_style_resize.html',
-      base_name + '_OffscreenCanvasTransferBeforeStyleResize',
-      test_rect=[0, 0, 350, 350],
-      revision=10,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_paint_after_resize.html',
-      base_name + '_OffscreenCanvasWebGLPaintAfterResize',
-      test_rect=[0, 0, 200, 200],
-      browser_args=browser_args,
-      revision=0, # Golden image revision is not used
-      tolerance=0,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'resized area',
-          'location': [1, 1],
-          'size': [48, 98],
-          'color': [0, 255, 0],
-        },
-        {
-          'comment': 'outside resized area',
-          'location': [51, 1],
-          'size': [48, 98],
-          'color': [255, 255, 255],
-        },
-      ]),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_transferToImageBitmap_main.html',
-      base_name + '_OffscreenCanvasTransferToImageBitmap',
-      test_rect=[0, 0, 300, 300],
-      revision=6,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_transferToImageBitmap_worker.html',
-      base_name + '_OffscreenCanvasTransferToImageBitmapWorker',
-      test_rect=[0, 0, 300, 300],
-      revision=6,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_commit_main.html',
-      base_name + '_OffscreenCanvasWebGLDefault',
-      test_rect=[0, 0, 360, 200],
-      revision=11,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_commit_worker.html',
-      base_name + '_OffscreenCanvasWebGLDefaultWorker',
-      test_rect=[0, 0, 360, 200],
-      revision=11,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_commit_main.html',
-      base_name + '_OffscreenCanvasWebGLSoftwareCompositing',
-      test_rect=[0, 0, 360, 200],
-      revision=7,
-      browser_args=browser_args + ['--disable-gpu-compositing']),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_commit_worker.html',
-      base_name + '_OffscreenCanvasWebGLSoftwareCompositingWorker',
-      test_rect=[0, 0, 360, 200],
-      revision=7,
-      browser_args=browser_args + ['--disable-gpu-compositing']),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_main.html',
-      base_name + '_OffscreenCanvasAccelerated2D',
-      test_rect=[0, 0, 360, 200],
-      revision=12,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_worker.html',
-      base_name + '_OffscreenCanvasAccelerated2DWorker',
-      test_rect=[0, 0, 360, 200],
-      revision=12,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_main.html',
-      base_name + '_OffscreenCanvasUnaccelerated2D',
-      test_rect=[0, 0, 360, 200],
-      revision=9,
-      browser_args=browser_args + unaccelerated_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_worker.html',
-      base_name + '_OffscreenCanvasUnaccelerated2DWorker',
-      test_rect=[0, 0, 360, 200],
-      revision=9,
-      browser_args=browser_args + unaccelerated_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_main.html',
-      base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing',
-      test_rect=[0, 0, 360, 200],
-      revision=14,
-      browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_commit_worker.html',
-      base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
-      test_rect=[0, 0, 360, 200],
-      revision=13,
-      browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_2d_resize_on_worker.html',
-      base_name + '_OffscreenCanvas2DResizeOnWorker',
-      test_rect=[0, 0, 200, 200],
-      revision=7,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_offscreenCanvas_webgl_resize_on_worker.html',
-      base_name + '_OffscreenCanvasWebglResizeOnWorker',
-      test_rect=[0, 0, 200, 200],
-      revision=9,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_canvas_display_linear-rgb.html',
-      base_name + '_CanvasDisplayLinearRGBAccelerated2D',
-      test_rect=[0, 0, 140, 140],
-      revision=10,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_canvas_display_linear-rgb.html',
-      base_name + '_CanvasDisplayLinearRGBUnaccelerated2D',
-      test_rect=[0, 0, 140, 140],
-      revision=2,
-      browser_args=browser_args + unaccelerated_args),
-
-    PixelTestPage(
-      'pixel_canvas_display_linear-rgb.html',
-      base_name + '_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing',
-      test_rect=[0, 0, 140, 140],
-      revision=8,
-      browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
-
-    PixelTestPage(
-      'pixel_canvas_display_srgb.html',
-      base_name + '_CanvasDisplaySRGBAccelerated2D',
-      test_rect=[0, 0, 140, 140],
-      revision=0, # not used, unsupported
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_canvas_display_srgb.html',
-      base_name + '_CanvasDisplaySRGBUnaccelerated2D',
-      test_rect=[0, 0, 140, 140],
-      revision=1,
-      browser_args=browser_args + unaccelerated_args),
-
-    PixelTestPage(
-      'pixel_canvas_display_srgb.html',
-      base_name + '_CanvasDisplaySRGBUnaccelerated2DGPUCompositing',
-      test_rect=[0, 0, 140, 140],
-      revision=1,
-      browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
-
-    PixelTestPage(
-      'pixel_canvas_low_latency_2d.html',
-      base_name + '_CanvasLowLatency2D',
-      test_rect=[0, 0, 100, 100],
-      revision=8,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_canvas_low_latency_2d.html',
-      base_name + '_CanvasUnacceleratedLowLatency2D',
-      test_rect=[0, 0, 100, 100],
-      revision=3,
-      browser_args=browser_args + unaccelerated_args),
-
-    PixelTestPage(
-      'pixel_canvas_low_latency_webgl.html',
-      base_name + '_CanvasLowLatencyWebGL',
-      test_rect=[0, 0, 200, 200],
-      revision=0, # not used
-      browser_args=browser_args,
-      tolerance=0,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          'location': [1, 1],
-          'size': [98, 98],
-          'color': [0, 255, 0],
-        },
-      ]),
-  ]
-
-# Only add these tests on platforms where SwiftShader is enabled.
-# Currently this is Windows and Linux.
-def SwiftShaderPages(base_name):
-  browser_args = ['--disable-gpu']
-  suffix = "_SwiftShader"
-  return [
-    PixelTestPage(
-      'pixel_canvas2d.html',
-      base_name + '_Canvas2DRedBox' + suffix,
-      test_rect=[0, 0, 300, 300],
-      revision=1,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_css3d.html',
-      base_name + '_CSS3DBlueBox' + suffix,
-      test_rect=[0, 0, 300, 300],
-      revision=2,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_webgl_aa_alpha.html',
-      base_name + '_WebGLGreenTriangle_AA_Alpha' + suffix,
-      test_rect=[0, 0, 300, 300],
-      revision=2,
-      browser_args=browser_args),
-
-    PixelTestPage(
-      'pixel_repeated_webgl_to_2d.html',
-      base_name + '_RepeatedWebGLTo2D' + suffix,
-      test_rect=[0, 0, 256, 256],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'green',
-          # 64x64 rectangle around the center at (128,128)
-          'location': [96, 96],
-          'size': [64, 64],
-          'color': [0, 255, 0],
-        },
-      ]),
-  ]
-
-# Test rendering where GPU process is blocked.
-def NoGpuProcessPages(base_name):
-  browser_args = ['--disable-gpu', '--disable-software-rasterizer']
-  suffix = "_NoGpuProcess"
-  return [
-    PixelTestPage(
-      'pixel_canvas2d.html',
-      base_name + '_Canvas2DRedBox' + suffix,
-      test_rect=[0, 0, 300, 300],
-      revision=2,
-      browser_args=browser_args,
-      gpu_process_disabled=True),
-
-    PixelTestPage(
-      'pixel_css3d.html',
-      base_name + '_CSS3DBlueBox' + suffix,
-      test_rect=[0, 0, 300, 300],
-      revision=2,
-      browser_args=browser_args,
-      gpu_process_disabled=True),
-  ]
-
-# Pages that should be run with various macOS specific command line
-# arguments.
-def MacSpecificPages(base_name):
-  iosurface_2d_canvas_args = [
-    '--enable-accelerated-2d-canvas']
-
-  non_chromium_image_args = ['--disable-webgl-image-chromium']
-
-  # This disables the Core Animation compositor, falling back to the
-  # old GLRenderer path, but continuing to allocate IOSurfaces for
-  # WebGL's back buffer.
-  no_overlays_args = ['--disable-mac-overlays']
-
-  return [
-    # On macOS, test the IOSurface 2D Canvas compositing path.
-    PixelTestPage(
-      'pixel_canvas2d_accelerated.html',
-      base_name + '_IOSurface2DCanvas',
-      test_rect=[0, 0, 400, 400],
-      revision=1,
-      browser_args=iosurface_2d_canvas_args),
-    PixelTestPage(
-      'pixel_canvas2d_webgl.html',
-      base_name + '_IOSurface2DCanvasWebGL',
-      test_rect=[0, 0, 300, 300],
-      revision=4,
-      browser_args=iosurface_2d_canvas_args),
-
-    # On macOS, test WebGL non-Chromium Image compositing path.
-    PixelTestPage(
-      'pixel_webgl_aa_alpha.html',
-      base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_Alpha',
-      test_rect=[0, 0, 300, 300],
-      revision=3,
-      browser_args=non_chromium_image_args),
-    PixelTestPage(
-      'pixel_webgl_noaa_alpha.html',
-      base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_Alpha',
-      test_rect=[0, 0, 300, 300],
-      revision=1,
-      browser_args=non_chromium_image_args),
-    PixelTestPage(
-      'pixel_webgl_aa_noalpha.html',
-      base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_NoAlpha',
-      test_rect=[0, 0, 300, 300],
-      revision=3,
-      browser_args=non_chromium_image_args),
-    PixelTestPage(
-      'pixel_webgl_noaa_noalpha.html',
-      base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha',
-      test_rect=[0, 0, 300, 300],
-      revision=1,
-      browser_args=non_chromium_image_args),
-
-    # On macOS, test CSS filter effects with and without the CA compositor.
-    PixelTestPage(
-      'filter_effects.html',
-      base_name + '_CSSFilterEffects',
-      test_rect=[0, 0, 300, 300],
-      revision=11),
-    PixelTestPage(
-      'filter_effects.html',
-      base_name + '_CSSFilterEffects_NoOverlays',
-      test_rect=[0, 0, 300, 300],
-      revision=11,
-      tolerance=10,
-      browser_args=no_overlays_args),
-
-    # Test WebGL's premultipliedAlpha:false without the CA compositor.
-    PixelTestPage(
-      'pixel_webgl_premultiplied_alpha_false.html',
-      base_name + '_WebGL_PremultipliedAlpha_False_NoOverlays',
-      test_rect=[0, 0, 150, 150],
-      revision=0, # Golden image revision is not used
-      browser_args=no_overlays_args,
-      tolerance=3,
-      expected_colors=[
-        SCALE_FACTOR_OVERRIDES,
-        {
-          'comment': 'brown',
-          'location': [1, 1],
-          'size': [148, 148],
-          # This is the color on an NVIDIA based MacBook Pro if the
-          # sRGB profile's applied correctly.
-          'color': [102, 77, 0],
-          # This is the color if it isn't.
-          # 'color': [101, 76, 12],
-        },
-      ]),
-  ]
-
-def DirectCompositionPages(base_name):
-  browser_args = ['--enable-direct-composition-layers']
-  browser_args_Underlay = browser_args + [
-    '--enable-features=DirectCompositionUnderlays']
-  browser_args_Nonroot = browser_args +[
-    '--enable-features=DirectCompositionNonrootOverlays,' +
-    'DirectCompositionUnderlays']
-  browser_args_Complex = browser_args + [
-    '--enable-features=DirectCompositionComplexOverlays,' +
-    'DirectCompositionNonrootOverlays,' +
-    'DirectCompositionUnderlays']
-  browser_args_YUY2 = browser_args + [
-    '--disable-features=DirectCompositionPreferNV12Overlays']
-  browser_args_DXVA = browser_args + [
-    '--disable-features=D3D11VideoDecoder']
-  browser_args_Underlay_DXVA = browser_args + [
-    '--enable-features=DirectCompositionUnderlays',
-    '--disable-features=D3D11VideoDecoder']
-
-  tolerance_dc = 5
-
-  return [
-    PixelTestPage(
-      'pixel_video_mp4.html',
-      base_name + '_DirectComposition_Video_MP4',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_mp4.html',
-      base_name + '_DirectComposition_Video_MP4_DXVA',
-      browser_args=browser_args_DXVA,
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_mp4_fullsize.html',
-      base_name + '_DirectComposition_Video_MP4_Fullsize',
-      browser_args=browser_args,
-      test_rect=[0, 0, 960, 540],
-      revision=0, # Golden image revision is not used
-      other_args={'zero_copy': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'top left video, yellow',
-          'location': [10, 10],
-          'size': [460, 250],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'top right video, red',
-          'location': [490, 10],
-          'size': [460, 250],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom left video, blue',
-          'location': [10, 280],
-          'size': [460, 250],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom right video, green',
-          'location': [490, 280],
-          'size': [460, 250],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_mp4.html',
-      base_name + '_DirectComposition_Video_MP4_YUY2',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_YUY2,
-      other_args={'expect_yuy2': True},
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_aspect_4x3.html',
-      base_name + '_DirectComposition_Video_MP4_FourColors_Aspect_4x3',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [28, 133],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [211, 1],
-          'size': [28, 133],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, yellow',
-          'location': [35, 5],
-          'size': [80, 57],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'top right video, red',
-          'location': [125, 5],
-          'size': [80, 57],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom left video, blue',
-          'location': [35, 73],
-          'size': [80, 57],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom right video, green',
-          'location': [125, 73],
-          'size': [80, 57],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_90.html',
-      base_name + '_DirectComposition_Video_MP4_FourColors_Rot_90',
-      test_rect=[0, 0, 427, 240],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      other_args={'video_is_rotated': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [282, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, red',
-          'location': [152, 5],
-          'size': [55, 110],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'top right video, green',
-          'location': [220, 5],
-          'size': [55, 110],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'bottom left video, yellow',
-          'location': [152, 125],
-          'size': [55, 110],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'bottom right video, blue',
-          'location': [220, 125],
-          'size': [55, 110],
-          'color': [12, 12, 255],
-        }]),
-
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_180.html',
-      base_name + '_DirectComposition_Video_MP4_FourColors_Rot_180',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      other_args={'video_is_rotated': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'top left video, green',
-          'location': [5, 5],
-          'size': [110, 57],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'top right video, blue',
-          'location': [125, 5],
-          'size': [110, 57],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom left video, red',
-          'location': [5, 72],
-          'size': [110, 57],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom right video, yellow',
-          'location': [125, 72],
-          'size': [110, 57],
-          'color': [255, 255, 15],
-        }]),
-
-    PixelTestPage(
-      'pixel_video_mp4_four_colors_rot_270.html',
-      base_name + '_DirectComposition_Video_MP4_FourColors_Rot_270',
-      test_rect=[0, 0, 427, 240],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      other_args={'video_is_rotated': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'outside video content, left side, white',
-          'location': [1, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'outside video content, right side, white',
-          'location': [282, 1],
-          'size': [144, 238],
-          'color': [255, 255, 255],
-        },
-        {
-          'comment': 'top left video, blue',
-          'location': [152, 5],
-          'size': [55, 110],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'top right video, yellow',
-          'location': [220, 5],
-          'size': [55, 110],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'bottom left video, green',
-          'location': [152, 125],
-          'size': [55, 110],
-          'color': [44, 255, 16],
-        },
-        {
-          'comment': 'bottom right video, red',
-          'location': [220, 125],
-          'size': [55, 110],
-          'color': [255, 17, 24],
-        }]),
-
-    PixelTestPage(
-      'pixel_video_vp9.html',
-      base_name + '_DirectComposition_Video_VP9',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_vp9.html',
-      base_name + '_DirectComposition_Video_VP9_DXVA',
-      browser_args=browser_args_DXVA,
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_vp9_fullsize.html',
-      base_name + '_DirectComposition_Video_VP9_Fullsize',
-      test_rect=[0, 0, 960, 540],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args,
-      other_args={'zero_copy': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'top left video, yellow',
-          'location': [10, 10],
-          'size': [460, 250],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'top right video, red',
-          'location': [490, 10],
-          'size': [460, 250],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'bottom left video, blue',
-          'location': [10, 280],
-          'size': [460, 250],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'bottom right video, green',
-          'location': [490, 280],
-          'size': [460, 250],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_vp9.html',
-      base_name + '_DirectComposition_Video_VP9_YUY2',
-      test_rect=[0, 0, 240, 135],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_YUY2,
-      other_args={'expect_yuy2': True},
-      tolerance=tolerance_dc,
-      expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
-
-    PixelTestPage(
-      'pixel_video_underlay.html',
-      base_name + '_DirectComposition_Underlay',
-      test_rect=[0, 0, 240, 136],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_Underlay,
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'black top left',
-          'location': [4, 4],
-          'size': [20, 20],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'yellow top left quadrant',
-          'location': [4, 34],
-          'size': [110, 30],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'red top right quadrant',
-          'location': [124, 4],
-          'size': [110, 60],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'blue bottom left quadrant',
-          'location': [4, 72],
-          'size': [110, 60],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'green bottom right quadrant',
-          'location': [124, 72],
-          'size': [110, 60],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_underlay.html',
-      base_name + '_DirectComposition_Underlay_DXVA',
-      test_rect=[0, 0, 240, 136],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_Underlay_DXVA,
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'black top left',
-          'location': [4, 4],
-          'size': [20, 20],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'yellow top left quadrant',
-          'location': [4, 34],
-          'size': [110, 30],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'red top right quadrant',
-          'location': [124, 4],
-          'size': [110, 60],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'blue bottom left quadrant',
-          'location': [4, 72],
-          'size': [110, 60],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'green bottom right quadrant',
-          'location': [124, 72],
-          'size': [110, 60],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_underlay_fullsize.html',
-      base_name + '_DirectComposition_Underlay_Fullsize',
-      test_rect=[0, 0, 960, 540],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_Underlay,
-      other_args={'zero_copy': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'black top left',
-          'location': [4, 4],
-          'size': [20, 20],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'yellow top left quadrant',
-          'location': [10, 35],
-          'size': [460, 225],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'red top right quadrant',
-          'location': [490, 10],
-          'size': [460, 250],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'blue bottom left quadrant',
-          'location': [10, 280],
-          'size': [460, 250],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'green bottom right quadrant',
-          'location': [490, 290],
-          'size': [460, 250],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_nonroot.html',
-      base_name + '_DirectComposition_Nonroot',
-      test_rect=[0, 0, 240, 136],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_Nonroot,
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'black top left',
-          'location': [4, 4],
-          'size': [20, 20],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'yellow top left quadrant',
-          'location': [4, 34],
-          'size': [110, 30],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'red top right quadrant',
-          'location': [124, 4],
-          'size': [50, 60],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'blue bottom left quadrant',
-          'location': [4, 72],
-          'size': [110, 60],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'green bottom right quadrant',
-          'location': [124, 72],
-          'size': [50, 60],
-          'color': [44, 255, 16],
-        }
-      ]),
-
-    PixelTestPage(
-      'pixel_video_complex_overlays.html',
-      base_name + '_DirectComposition_ComplexOverlays',
-      test_rect=[0, 0, 240, 136],
-      revision=0, # Golden image revision is not used
-      browser_args=browser_args_Complex,
-      other_args={'video_is_rotated': True},
-      tolerance=tolerance_dc,
-      expected_colors=[
-        {
-          'comment': 'black top left',
-          'location': [4, 4],
-          'size': [20, 20],
-          'color': [0, 0, 0],
-        },
-        {
-          'comment': 'yellow top left quadrant',
-          'location': [60, 10],
-          'size': [65, 30],
-          'color': [255, 255, 15],
-        },
-        {
-          'comment': 'red top right quadrant',
-          'location': [150, 45],
-          'size': [65, 30],
-          'color': [255, 17, 24],
-        },
-        {
-          'comment': 'blue bottom left quadrant',
-          'location': [30, 70],
-          'size': [65, 30],
-          'color': [12, 12, 255],
-        },
-        {
-          'comment': 'green bottom right quadrant',
-          'location': [130, 100],
-          'size': [65, 30],
-          'color': [44, 255, 16],
-        }]),
+      PixelTestPage(
+        'pixel_repeated_webgl_to_2d.html',
+        base_name + '_RepeatedWebGLTo2D_SoftwareCompositing',
+        test_rect=[0, 0, 256, 256],
+        revision=0, # Golden image revision is not used
+        browser_args=sw_compositing_args,
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            # 64x64 rectangle around the center at (128,128)
+            'location': [96, 96],
+            'size': [64, 64],
+            'color': [0, 255, 0],
+          },
+        ]),
     ]
+
+
+  # Pages that should be run with GPU rasterization enabled.
+  @staticmethod
+  def GpuRasterizationPages(base_name):
+    browser_args = ['--force-gpu-rasterization']
+    return [
+      PixelTestPage(
+        'pixel_background.html',
+        base_name + '_GpuRasterization_BlueBox',
+        test_rect=[0, 0, 220, 220],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=0,
+        expected_colors=[
+          {
+            'comment': 'body-t',
+            'location': [5, 5],
+            'size': [1, 1],
+            'color': [0, 128, 0],
+          },
+          {
+            'comment': 'body-r',
+            'location': [215, 5],
+            'size': [1, 1],
+            'color': [0, 128, 0],
+          },
+          {
+            'comment': 'body-b',
+            'location': [215, 215],
+            'size': [1, 1],
+            'color': [0, 128, 0],
+          },
+          {
+            'comment': 'body-l',
+            'location': [5, 215],
+            'size': [1, 1],
+            'color': [0, 128, 0],
+          },
+          {
+            'comment': 'background-t',
+            'location': [30, 30],
+            'size': [1, 1],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'background-r',
+            'location': [170, 30],
+            'size': [1, 1],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'background-b',
+            'location': [170, 170],
+            'size': [1, 1],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'background-l',
+            'location': [30, 170],
+            'size': [1, 1],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'box-t',
+            'location': [70, 70],
+            'size': [1, 1],
+            'color': [0, 0, 255],
+          },
+          {
+            'comment': 'box-r',
+            'location': [140, 70],
+            'size': [1, 1],
+            'color': [0, 0, 255],
+          },
+          {
+            'comment': 'box-b',
+            'location': [140, 140],
+            'size': [1, 1],
+            'color': [0, 0, 255],
+          },
+          {
+            'comment': 'box-l',
+            'location': [70, 140],
+            'size': [1, 1],
+            'color': [0, 0, 255],
+          }
+        ]),
+      PixelTestPage(
+        'concave_paths.html',
+        base_name + '_GpuRasterization_ConcavePaths',
+        test_rect=[0, 0, 100, 100],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=0,
+        expected_colors=[
+          {
+            'comment': 'outside',
+            'location': [80, 60],
+            'size': [1, 1],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside',
+            'location': [28, 20],
+            'size': [1, 1],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'inside',
+            'location': [32, 25],
+            'size': [1, 1],
+            'color': [255, 215, 0],
+          },
+          {
+            'comment': 'inside',
+            'location': [80, 80],
+            'size': [1, 1],
+            'color': [255, 215, 0],
+          }
+        ])
+    ]
+
+  # Pages that should be run with experimental canvas features.
+  @staticmethod
+  def ExperimentalCanvasFeaturesPages(base_name):
+    browser_args = [
+      '--enable-experimental-web-platform-features'] # for lowLatency
+    unaccelerated_args = [
+      '--disable-accelerated-2d-canvas',
+      '--disable-gpu-compositing']
+
+    return [
+      PixelTestPage(
+        'pixel_offscreenCanvas_transfer_after_style_resize.html',
+        base_name + '_OffscreenCanvasTransferAfterStyleResize',
+        test_rect=[0, 0, 350, 350],
+        revision=10,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_transfer_before_style_resize.html',
+        base_name + '_OffscreenCanvasTransferBeforeStyleResize',
+        test_rect=[0, 0, 350, 350],
+        revision=10,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_paint_after_resize.html',
+        base_name + '_OffscreenCanvasWebGLPaintAfterResize',
+        test_rect=[0, 0, 200, 200],
+        browser_args=browser_args,
+        revision=0, # Golden image revision is not used
+        tolerance=0,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'resized area',
+            'location': [1, 1],
+            'size': [48, 98],
+            'color': [0, 255, 0],
+          },
+          {
+            'comment': 'outside resized area',
+            'location': [51, 1],
+            'size': [48, 98],
+            'color': [255, 255, 255],
+          },
+        ]),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_transferToImageBitmap_main.html',
+        base_name + '_OffscreenCanvasTransferToImageBitmap',
+        test_rect=[0, 0, 300, 300],
+        revision=6,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_transferToImageBitmap_worker.html',
+        base_name + '_OffscreenCanvasTransferToImageBitmapWorker',
+        test_rect=[0, 0, 300, 300],
+        revision=6,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_commit_main.html',
+        base_name + '_OffscreenCanvasWebGLDefault',
+        test_rect=[0, 0, 360, 200],
+        revision=11,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_commit_worker.html',
+        base_name + '_OffscreenCanvasWebGLDefaultWorker',
+        test_rect=[0, 0, 360, 200],
+        revision=11,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_commit_main.html',
+        base_name + '_OffscreenCanvasWebGLSoftwareCompositing',
+        test_rect=[0, 0, 360, 200],
+        revision=7,
+        browser_args=browser_args + ['--disable-gpu-compositing']),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_commit_worker.html',
+        base_name + '_OffscreenCanvasWebGLSoftwareCompositingWorker',
+        test_rect=[0, 0, 360, 200],
+        revision=7,
+        browser_args=browser_args + ['--disable-gpu-compositing']),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_main.html',
+        base_name + '_OffscreenCanvasAccelerated2D',
+        test_rect=[0, 0, 360, 200],
+        revision=12,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_worker.html',
+        base_name + '_OffscreenCanvasAccelerated2DWorker',
+        test_rect=[0, 0, 360, 200],
+        revision=12,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_main.html',
+        base_name + '_OffscreenCanvasUnaccelerated2D',
+        test_rect=[0, 0, 360, 200],
+        revision=9,
+        browser_args=browser_args + unaccelerated_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_worker.html',
+        base_name + '_OffscreenCanvasUnaccelerated2DWorker',
+        test_rect=[0, 0, 360, 200],
+        revision=9,
+        browser_args=browser_args + unaccelerated_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_main.html',
+        base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing',
+        test_rect=[0, 0, 360, 200],
+        revision=14,
+        browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_commit_worker.html',
+        base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
+        test_rect=[0, 0, 360, 200],
+        revision=13,
+        browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_2d_resize_on_worker.html',
+        base_name + '_OffscreenCanvas2DResizeOnWorker',
+        test_rect=[0, 0, 200, 200],
+        revision=7,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_offscreenCanvas_webgl_resize_on_worker.html',
+        base_name + '_OffscreenCanvasWebglResizeOnWorker',
+        test_rect=[0, 0, 200, 200],
+        revision=9,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_canvas_display_linear-rgb.html',
+        base_name + '_CanvasDisplayLinearRGBAccelerated2D',
+        test_rect=[0, 0, 140, 140],
+        revision=10,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_canvas_display_linear-rgb.html',
+        base_name + '_CanvasDisplayLinearRGBUnaccelerated2D',
+        test_rect=[0, 0, 140, 140],
+        revision=2,
+        browser_args=browser_args + unaccelerated_args),
+
+      PixelTestPage(
+        'pixel_canvas_display_linear-rgb.html',
+        base_name + '_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing',
+        test_rect=[0, 0, 140, 140],
+        revision=8,
+        browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
+
+      PixelTestPage(
+        'pixel_canvas_display_srgb.html',
+        base_name + '_CanvasDisplaySRGBAccelerated2D',
+        test_rect=[0, 0, 140, 140],
+        revision=0, # not used, unsupported
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_canvas_display_srgb.html',
+        base_name + '_CanvasDisplaySRGBUnaccelerated2D',
+        test_rect=[0, 0, 140, 140],
+        revision=1,
+        browser_args=browser_args + unaccelerated_args),
+
+      PixelTestPage(
+        'pixel_canvas_display_srgb.html',
+        base_name + '_CanvasDisplaySRGBUnaccelerated2DGPUCompositing',
+        test_rect=[0, 0, 140, 140],
+        revision=1,
+        browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
+
+      PixelTestPage(
+        'pixel_canvas_low_latency_2d.html',
+        base_name + '_CanvasLowLatency2D',
+        test_rect=[0, 0, 100, 100],
+        revision=8,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_canvas_low_latency_2d.html',
+        base_name + '_CanvasUnacceleratedLowLatency2D',
+        test_rect=[0, 0, 100, 100],
+        revision=3,
+        browser_args=browser_args + unaccelerated_args),
+
+      PixelTestPage(
+        'pixel_canvas_low_latency_webgl.html',
+        base_name + '_CanvasLowLatencyWebGL',
+        test_rect=[0, 0, 200, 200],
+        revision=0, # not used
+        browser_args=browser_args,
+        tolerance=0,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            'location': [1, 1],
+            'size': [98, 98],
+            'color': [0, 255, 0],
+          },
+        ]),
+    ]
+
+  # Only add these tests on platforms where SwiftShader is enabled.
+  # Currently this is Windows and Linux.
+  @staticmethod
+  def SwiftShaderPages(base_name):
+    browser_args = ['--disable-gpu']
+    suffix = "_SwiftShader"
+    return [
+      PixelTestPage(
+        'pixel_canvas2d.html',
+        base_name + '_Canvas2DRedBox' + suffix,
+        test_rect=[0, 0, 300, 300],
+        revision=1,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_css3d.html',
+        base_name + '_CSS3DBlueBox' + suffix,
+        test_rect=[0, 0, 300, 300],
+        revision=2,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_webgl_aa_alpha.html',
+        base_name + '_WebGLGreenTriangle_AA_Alpha' + suffix,
+        test_rect=[0, 0, 300, 300],
+        revision=2,
+        browser_args=browser_args),
+
+      PixelTestPage(
+        'pixel_repeated_webgl_to_2d.html',
+        base_name + '_RepeatedWebGLTo2D' + suffix,
+        test_rect=[0, 0, 256, 256],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'green',
+            # 64x64 rectangle around the center at (128,128)
+            'location': [96, 96],
+            'size': [64, 64],
+            'color': [0, 255, 0],
+          },
+        ]),
+    ]
+
+  # Test rendering where GPU process is blocked.
+  @staticmethod
+  def NoGpuProcessPages(base_name):
+    browser_args = ['--disable-gpu', '--disable-software-rasterizer']
+    suffix = "_NoGpuProcess"
+    return [
+      PixelTestPage(
+        'pixel_canvas2d.html',
+        base_name + '_Canvas2DRedBox' + suffix,
+        test_rect=[0, 0, 300, 300],
+        revision=2,
+        browser_args=browser_args,
+        gpu_process_disabled=True),
+
+      PixelTestPage(
+        'pixel_css3d.html',
+        base_name + '_CSS3DBlueBox' + suffix,
+        test_rect=[0, 0, 300, 300],
+        revision=2,
+        browser_args=browser_args,
+        gpu_process_disabled=True),
+    ]
+
+  # Pages that should be run with various macOS specific command line
+  # arguments.
+  @staticmethod
+  def MacSpecificPages(base_name):
+    iosurface_2d_canvas_args = [
+      '--enable-accelerated-2d-canvas']
+
+    non_chromium_image_args = ['--disable-webgl-image-chromium']
+
+    # This disables the Core Animation compositor, falling back to the
+    # old GLRenderer path, but continuing to allocate IOSurfaces for
+    # WebGL's back buffer.
+    no_overlays_args = ['--disable-mac-overlays']
+
+    return [
+      # On macOS, test the IOSurface 2D Canvas compositing path.
+      PixelTestPage(
+        'pixel_canvas2d_accelerated.html',
+        base_name + '_IOSurface2DCanvas',
+        test_rect=[0, 0, 400, 400],
+        revision=1,
+        browser_args=iosurface_2d_canvas_args),
+      PixelTestPage(
+        'pixel_canvas2d_webgl.html',
+        base_name + '_IOSurface2DCanvasWebGL',
+        test_rect=[0, 0, 300, 300],
+        revision=4,
+        browser_args=iosurface_2d_canvas_args),
+
+      # On macOS, test WebGL non-Chromium Image compositing path.
+      PixelTestPage(
+        'pixel_webgl_aa_alpha.html',
+        base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_Alpha',
+        test_rect=[0, 0, 300, 300],
+        revision=3,
+        browser_args=non_chromium_image_args),
+      PixelTestPage(
+        'pixel_webgl_noaa_alpha.html',
+        base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_Alpha',
+        test_rect=[0, 0, 300, 300],
+        revision=1,
+        browser_args=non_chromium_image_args),
+      PixelTestPage(
+        'pixel_webgl_aa_noalpha.html',
+        base_name + '_WebGLGreenTriangle_NonChromiumImage_AA_NoAlpha',
+        test_rect=[0, 0, 300, 300],
+        revision=3,
+        browser_args=non_chromium_image_args),
+      PixelTestPage(
+        'pixel_webgl_noaa_noalpha.html',
+        base_name + '_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha',
+        test_rect=[0, 0, 300, 300],
+        revision=1,
+        browser_args=non_chromium_image_args),
+
+      # On macOS, test CSS filter effects with and without the CA compositor.
+      PixelTestPage(
+        'filter_effects.html',
+        base_name + '_CSSFilterEffects',
+        test_rect=[0, 0, 300, 300],
+        revision=11),
+      PixelTestPage(
+        'filter_effects.html',
+        base_name + '_CSSFilterEffects_NoOverlays',
+        test_rect=[0, 0, 300, 300],
+        revision=11,
+        tolerance=10,
+        browser_args=no_overlays_args),
+
+      # Test WebGL's premultipliedAlpha:false without the CA compositor.
+      PixelTestPage(
+        'pixel_webgl_premultiplied_alpha_false.html',
+        base_name + '_WebGL_PremultipliedAlpha_False_NoOverlays',
+        test_rect=[0, 0, 150, 150],
+        revision=0, # Golden image revision is not used
+        browser_args=no_overlays_args,
+        tolerance=3,
+        expected_colors=[
+          SCALE_FACTOR_OVERRIDES,
+          {
+            'comment': 'brown',
+            'location': [1, 1],
+            'size': [148, 148],
+            # This is the color on an NVIDIA based MacBook Pro if the
+            # sRGB profile's applied correctly.
+            'color': [102, 77, 0],
+            # This is the color if it isn't.
+            # 'color': [101, 76, 12],
+          },
+        ]),
+    ]
+
+  @staticmethod
+  def DirectCompositionPages(base_name):
+    browser_args = ['--enable-direct-composition-layers']
+    browser_args_Underlay = browser_args + [
+      '--enable-features=DirectCompositionUnderlays']
+    browser_args_Nonroot = browser_args +[
+      '--enable-features=DirectCompositionNonrootOverlays,' +
+      'DirectCompositionUnderlays']
+    browser_args_Complex = browser_args + [
+      '--enable-features=DirectCompositionComplexOverlays,' +
+      'DirectCompositionNonrootOverlays,' +
+      'DirectCompositionUnderlays']
+    browser_args_YUY2 = browser_args + [
+      '--disable-features=DirectCompositionPreferNV12Overlays']
+    browser_args_DXVA = browser_args + [
+      '--disable-features=D3D11VideoDecoder']
+    browser_args_Underlay_DXVA = browser_args + [
+      '--enable-features=DirectCompositionUnderlays',
+      '--disable-features=D3D11VideoDecoder']
+
+    tolerance_dc = 5
+
+    return [
+      PixelTestPage(
+        'pixel_video_mp4.html',
+        base_name + '_DirectComposition_Video_MP4',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_mp4.html',
+        base_name + '_DirectComposition_Video_MP4_DXVA',
+        browser_args=browser_args_DXVA,
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_mp4_fullsize.html',
+        base_name + '_DirectComposition_Video_MP4_Fullsize',
+        browser_args=browser_args,
+        test_rect=[0, 0, 960, 540],
+        revision=0, # Golden image revision is not used
+        other_args={'zero_copy': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'top left video, yellow',
+            'location': [10, 10],
+            'size': [460, 250],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'top right video, red',
+            'location': [490, 10],
+            'size': [460, 250],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom left video, blue',
+            'location': [10, 280],
+            'size': [460, 250],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom right video, green',
+            'location': [490, 280],
+            'size': [460, 250],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_mp4.html',
+        base_name + '_DirectComposition_Video_MP4_YUY2',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_YUY2,
+        other_args={'expect_yuy2': True},
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_aspect_4x3.html',
+        base_name + '_DirectComposition_Video_MP4_FourColors_Aspect_4x3',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [28, 133],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [211, 1],
+            'size': [28, 133],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, yellow',
+            'location': [35, 5],
+            'size': [80, 57],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'top right video, red',
+            'location': [125, 5],
+            'size': [80, 57],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom left video, blue',
+            'location': [35, 73],
+            'size': [80, 57],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom right video, green',
+            'location': [125, 73],
+            'size': [80, 57],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_90.html',
+        base_name + '_DirectComposition_Video_MP4_FourColors_Rot_90',
+        test_rect=[0, 0, 427, 240],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        other_args={'video_is_rotated': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [282, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, red',
+            'location': [152, 5],
+            'size': [55, 110],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'top right video, green',
+            'location': [220, 5],
+            'size': [55, 110],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'bottom left video, yellow',
+            'location': [152, 125],
+            'size': [55, 110],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'bottom right video, blue',
+            'location': [220, 125],
+            'size': [55, 110],
+            'color': [12, 12, 255],
+          }]),
+
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_180.html',
+        base_name + '_DirectComposition_Video_MP4_FourColors_Rot_180',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        other_args={'video_is_rotated': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'top left video, green',
+            'location': [5, 5],
+            'size': [110, 57],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'top right video, blue',
+            'location': [125, 5],
+            'size': [110, 57],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom left video, red',
+            'location': [5, 72],
+            'size': [110, 57],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom right video, yellow',
+            'location': [125, 72],
+            'size': [110, 57],
+            'color': [255, 255, 15],
+          }]),
+
+      PixelTestPage(
+        'pixel_video_mp4_four_colors_rot_270.html',
+        base_name + '_DirectComposition_Video_MP4_FourColors_Rot_270',
+        test_rect=[0, 0, 427, 240],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        other_args={'video_is_rotated': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'outside video content, left side, white',
+            'location': [1, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'outside video content, right side, white',
+            'location': [282, 1],
+            'size': [144, 238],
+            'color': [255, 255, 255],
+          },
+          {
+            'comment': 'top left video, blue',
+            'location': [152, 5],
+            'size': [55, 110],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'top right video, yellow',
+            'location': [220, 5],
+            'size': [55, 110],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'bottom left video, green',
+            'location': [152, 125],
+            'size': [55, 110],
+            'color': [44, 255, 16],
+          },
+          {
+            'comment': 'bottom right video, red',
+            'location': [220, 125],
+            'size': [55, 110],
+            'color': [255, 17, 24],
+          }]),
+
+      PixelTestPage(
+        'pixel_video_vp9.html',
+        base_name + '_DirectComposition_Video_VP9',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_vp9.html',
+        base_name + '_DirectComposition_Video_VP9_DXVA',
+        browser_args=browser_args_DXVA,
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_vp9_fullsize.html',
+        base_name + '_DirectComposition_Video_VP9_Fullsize',
+        test_rect=[0, 0, 960, 540],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args,
+        other_args={'zero_copy': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'top left video, yellow',
+            'location': [10, 10],
+            'size': [460, 250],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'top right video, red',
+            'location': [490, 10],
+            'size': [460, 250],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'bottom left video, blue',
+            'location': [10, 280],
+            'size': [460, 250],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'bottom right video, green',
+            'location': [490, 280],
+            'size': [460, 250],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_vp9.html',
+        base_name + '_DirectComposition_Video_VP9_YUY2',
+        test_rect=[0, 0, 240, 135],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_YUY2,
+        other_args={'expect_yuy2': True},
+        tolerance=tolerance_dc,
+        expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
+
+      PixelTestPage(
+        'pixel_video_underlay.html',
+        base_name + '_DirectComposition_Underlay',
+        test_rect=[0, 0, 240, 136],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_Underlay,
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'black top left',
+            'location': [4, 4],
+            'size': [20, 20],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'yellow top left quadrant',
+            'location': [4, 34],
+            'size': [110, 30],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'red top right quadrant',
+            'location': [124, 4],
+            'size': [110, 60],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'blue bottom left quadrant',
+            'location': [4, 72],
+            'size': [110, 60],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'green bottom right quadrant',
+            'location': [124, 72],
+            'size': [110, 60],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_underlay.html',
+        base_name + '_DirectComposition_Underlay_DXVA',
+        test_rect=[0, 0, 240, 136],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_Underlay_DXVA,
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'black top left',
+            'location': [4, 4],
+            'size': [20, 20],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'yellow top left quadrant',
+            'location': [4, 34],
+            'size': [110, 30],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'red top right quadrant',
+            'location': [124, 4],
+            'size': [110, 60],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'blue bottom left quadrant',
+            'location': [4, 72],
+            'size': [110, 60],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'green bottom right quadrant',
+            'location': [124, 72],
+            'size': [110, 60],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_underlay_fullsize.html',
+        base_name + '_DirectComposition_Underlay_Fullsize',
+        test_rect=[0, 0, 960, 540],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_Underlay,
+        other_args={'zero_copy': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'black top left',
+            'location': [4, 4],
+            'size': [20, 20],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'yellow top left quadrant',
+            'location': [10, 35],
+            'size': [460, 225],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'red top right quadrant',
+            'location': [490, 10],
+            'size': [460, 250],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'blue bottom left quadrant',
+            'location': [10, 280],
+            'size': [460, 250],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'green bottom right quadrant',
+            'location': [490, 290],
+            'size': [460, 250],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_nonroot.html',
+        base_name + '_DirectComposition_Nonroot',
+        test_rect=[0, 0, 240, 136],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_Nonroot,
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'black top left',
+            'location': [4, 4],
+            'size': [20, 20],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'yellow top left quadrant',
+            'location': [4, 34],
+            'size': [110, 30],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'red top right quadrant',
+            'location': [124, 4],
+            'size': [50, 60],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'blue bottom left quadrant',
+            'location': [4, 72],
+            'size': [110, 60],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'green bottom right quadrant',
+            'location': [124, 72],
+            'size': [50, 60],
+            'color': [44, 255, 16],
+          }
+        ]),
+
+      PixelTestPage(
+        'pixel_video_complex_overlays.html',
+        base_name + '_DirectComposition_ComplexOverlays',
+        test_rect=[0, 0, 240, 136],
+        revision=0, # Golden image revision is not used
+        browser_args=browser_args_Complex,
+        other_args={'video_is_rotated': True},
+        tolerance=tolerance_dc,
+        expected_colors=[
+          {
+            'comment': 'black top left',
+            'location': [4, 4],
+            'size': [20, 20],
+            'color': [0, 0, 0],
+          },
+          {
+            'comment': 'yellow top left quadrant',
+            'location': [60, 10],
+            'size': [65, 30],
+            'color': [255, 255, 15],
+          },
+          {
+            'comment': 'red top right quadrant',
+            'location': [150, 45],
+            'size': [65, 30],
+            'color': [255, 17, 24],
+          },
+          {
+            'comment': 'blue bottom left quadrant',
+            'location': [30, 70],
+            'size': [65, 30],
+            'color': [12, 12, 255],
+          },
+          {
+            'comment': 'green bottom right quadrant',
+            'location': [130, 100],
+            'size': [65, 30],
+            'color': [44, 255, 16],
+          }]),
+      ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 3235591..1c1d50c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -17,7 +17,6 @@
 [ android ] Pixel_OffscreenCanvasWebGLSoftwareCompositing [ Skip ]
 [ android ] Pixel_OffscreenCanvasWebGLSoftwareCompositingWorker [ Skip ]
 [ android ] Pixel_CanvasDisplayLinearRGBUnaccelerated2D [ Skip ]
-[ android ] Pixel_CanvasDisplaySRGBUnaccelerated2D [ Skip ]
 [ android ] Pixel_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing [ Skip ]
 [ android ] Pixel_CanvasDisplaySRGBUnaccelerated2DGPUCompositing [ Skip ]
 [ android ] Pixel_CanvasDisplaySRGBUnaccelerated2D [ Skip ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py
index 24824658..aa1e326 100644
--- a/content/test/gpu/gpu_tests/trace_integration_test.py
+++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -104,21 +104,22 @@
     # Include the device level trace tests, even though they're
     # currently skipped on all platforms, to give a hint that they
     # should perhaps be enabled in the future.
-    for p in pixel_test_pages.DefaultPages('TraceTest'):
+    namespace = pixel_test_pages.PixelTestPages
+    for p in namespace.DefaultPages('TraceTest'):
       yield (p.name, gpu_relative_path + p.url,
              {'browser_args': [],
               'category': cls._DisabledByDefaultTraceCategory('gpu.service'),
               'test_harness_script': webgl_test_harness_script,
               'finish_js_condition': 'domAutomationController._finished',
               'success_eval_func': 'CheckGLCategory'})
-    for p in pixel_test_pages.DefaultPages('DeviceTraceTest'):
+    for p in namespace.DefaultPages('DeviceTraceTest'):
       yield (p.name, gpu_relative_path + p.url,
              {'browser_args': [],
               'category': cls._DisabledByDefaultTraceCategory('gpu.device'),
               'test_harness_script': webgl_test_harness_script,
               'finish_js_condition': 'domAutomationController._finished',
               'success_eval_func': 'CheckGLCategory'})
-    for p in pixel_test_pages.DirectCompositionPages('VideoPathTraceTest'):
+    for p in namespace.DirectCompositionPages('VideoPathTraceTest'):
       yield (p.name, gpu_relative_path + p.url,
              {'browser_args': p.browser_args,
               'category': cls._DisabledByDefaultTraceCategory('gpu.service'),
@@ -126,7 +127,7 @@
               'finish_js_condition': 'domAutomationController._finished',
               'success_eval_func': 'CheckVideoPath',
               'other_args': p.other_args})
-    for p in pixel_test_pages.DirectCompositionPages('OverlayModeTraceTest'):
+    for p in namespace.DirectCompositionPages('OverlayModeTraceTest'):
       if p.other_args and p.other_args.get('video_is_rotated', False):
         # For all drivers we tested, when a video is rotated, frames won't
         # be promoted to hardware overlays.
diff --git a/content/test/mock_clipboard_host.cc b/content/test/mock_clipboard_host.cc
index b794fc6..fab0d96 100644
--- a/content/test/mock_clipboard_host.cc
+++ b/content/test/mock_clipboard_host.cc
@@ -98,15 +98,13 @@
                                                    : base::string16());
 }
 
-void MockClipboardHost::WriteText(ui::ClipboardType,
-                                  const base::string16& text) {
+void MockClipboardHost::WriteText(const base::string16& text) {
   if (needs_reset_)
     Reset();
   plain_text_ = text;
 }
 
-void MockClipboardHost::WriteHtml(ui::ClipboardType,
-                                  const base::string16& markup,
+void MockClipboardHost::WriteHtml(const base::string16& markup,
                                   const GURL& url) {
   if (needs_reset_)
     Reset();
@@ -114,14 +112,13 @@
   url_ = url;
 }
 
-void MockClipboardHost::WriteSmartPasteMarker(ui::ClipboardType) {
+void MockClipboardHost::WriteSmartPasteMarker() {
   if (needs_reset_)
     Reset();
   write_smart_paste_ = true;
 }
 
 void MockClipboardHost::WriteCustomData(
-    ui::ClipboardType,
     const base::flat_map<base::string16, base::string16>& data) {
   if (needs_reset_)
     Reset();
@@ -129,17 +126,16 @@
     custom_data_[it.first] = it.second;
 }
 
-void MockClipboardHost::WriteBookmark(ui::ClipboardType,
-                                      const std::string& url,
+void MockClipboardHost::WriteBookmark(const std::string& url,
                                       const base::string16& title) {}
 
-void MockClipboardHost::WriteImage(ui::ClipboardType, const SkBitmap& bitmap) {
+void MockClipboardHost::WriteImage(const SkBitmap& bitmap) {
   if (needs_reset_)
     Reset();
   image_ = bitmap;
 }
 
-void MockClipboardHost::CommitWrite(ui::ClipboardType) {
+void MockClipboardHost::CommitWrite() {
   ++sequence_number_;
   needs_reset_ = true;
 }
diff --git a/content/test/mock_clipboard_host.h b/content/test/mock_clipboard_host.h
index 81f0ad025..da643701 100644
--- a/content/test/mock_clipboard_host.h
+++ b/content/test/mock_clipboard_host.h
@@ -42,21 +42,15 @@
   void ReadCustomData(ui::ClipboardType clipboard_type,
                       const base::string16& type,
                       ReadCustomDataCallback callback) override;
-  void WriteText(ui::ClipboardType clipboard_type,
-                 const base::string16& text) override;
-  void WriteHtml(ui::ClipboardType clipboard_type,
-                 const base::string16& markup,
-                 const GURL& url) override;
-  void WriteSmartPasteMarker(ui::ClipboardType clipboard_type) override;
+  void WriteText(const base::string16& text) override;
+  void WriteHtml(const base::string16& markup, const GURL& url) override;
+  void WriteSmartPasteMarker() override;
   void WriteCustomData(
-      ui::ClipboardType clipboard_type,
       const base::flat_map<base::string16, base::string16>& data) override;
-  void WriteBookmark(ui::ClipboardType clipboard_type,
-                     const std::string& url,
+  void WriteBookmark(const std::string& url,
                      const base::string16& title) override;
-  void WriteImage(ui::ClipboardType clipboard_type,
-                  const SkBitmap& bitmap) override;
-  void CommitWrite(ui::ClipboardType clipboard_type) override;
+  void WriteImage(const SkBitmap& bitmap) override;
+  void CommitWrite() override;
 #if defined(OS_MACOSX)
   void WriteStringToFindPboard(const base::string16& text) override;
 #endif
diff --git a/fuchsia/base/test_navigation_listener.cc b/fuchsia/base/test_navigation_listener.cc
index 1cad513..d86f1a36 100644
--- a/fuchsia/base/test_navigation_listener.cc
+++ b/fuchsia/base/test_navigation_listener.cc
@@ -59,10 +59,23 @@
 
 void TestNavigationListener::RunUntilUrlAndTitleEquals(
     const GURL& expected_url,
-    const std::string& expected_title) {
+    const base::StringPiece expected_title) {
   fuchsia::web::NavigationState state;
   state.set_url(expected_url.spec());
-  state.set_title(expected_title);
+  state.set_title(expected_title.as_string());
+  RunUntilNavigationStateMatches(state);
+}
+
+void TestNavigationListener::RunUntilUrlTitleBackForwardEquals(
+    const GURL& expected_url,
+    base::StringPiece expected_title,
+    bool expected_can_go_back,
+    bool expected_can_go_forward) {
+  fuchsia::web::NavigationState state;
+  state.set_url(expected_url.spec());
+  state.set_title(expected_title.as_string());
+  state.set_can_go_back(expected_can_go_back);
+  state.set_can_go_forward(expected_can_go_forward);
   RunUntilNavigationStateMatches(state);
 }
 
@@ -76,10 +89,10 @@
     current_state_.set_url(change.url());
   if (change.has_title())
     current_state_.set_title(change.title());
-  if (change.has_can_go_forward())
-    current_state_.set_can_go_forward(change.can_go_forward());
   if (change.has_can_go_back())
     current_state_.set_can_go_back(change.can_go_back());
+  if (change.has_can_go_forward())
+    current_state_.set_can_go_forward(change.can_go_forward());
 
   // Signal readiness for the next navigation event.
   before_ack_.Run(change, std::move(callback));
@@ -105,18 +118,18 @@
       all_equal = false;
     }
   }
-  if (expected.has_can_go_forward()) {
-    if (!current_state_.has_can_go_forward() ||
-        expected.can_go_forward() != current_state_.can_go_forward()) {
-      all_equal = false;
-    }
-  }
   if (expected.has_can_go_back()) {
     if (!current_state_.has_can_go_back() ||
         expected.can_go_back() != current_state_.can_go_back()) {
       all_equal = false;
     }
   }
+  if (expected.has_can_go_forward()) {
+    if (!current_state_.has_can_go_forward() ||
+        expected.can_go_forward() != current_state_.can_go_forward()) {
+      all_equal = false;
+    }
+  }
 
   return all_equal;
 }
diff --git a/fuchsia/base/test_navigation_listener.h b/fuchsia/base/test_navigation_listener.h
index e4414a9..9087f3d5 100644
--- a/fuchsia/base/test_navigation_listener.h
+++ b/fuchsia/base/test_navigation_listener.h
@@ -37,7 +37,14 @@
   // Calls RunUntilNavigationStateMatches with a NagivationState that has
   // |expected_url| and |expected_title|.
   void RunUntilUrlAndTitleEquals(const GURL& expected_url,
-                                 const std::string& expected_title);
+                                 base::StringPiece expected_title);
+
+  // Calls RunUntilNavigationStateMatches with a NagivationState that has
+  // all the expected fields.
+  void RunUntilUrlTitleBackForwardEquals(const GURL& expected_url,
+                                         base::StringPiece expected_title,
+                                         bool expected_can_go_back,
+                                         bool expected_can_go_forward);
 
   // Register a callback which intercepts the execution of the event
   // acknowledgement callback. |before_ack| takes ownership of the
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index b2cf15dd..f5cb6e0 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -90,6 +90,9 @@
   data_deps = [
     ":web_engine_pak",
   ]
+  public_deps = [
+    "//content/public/browser",
+  ]
   data = [
     "$root_out_dir/web_engine.pak",
   ]
@@ -214,6 +217,7 @@
 
 test("web_engine_unittests") {
   sources = [
+    "browser/frame_impl_unittest.cc",
     "context_provider_impl_unittest.cc",
     "fake_context.cc",
     "fake_context.h",
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index e94f426..842a69a7 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -78,7 +78,8 @@
 };
 
 fuchsia::web::NavigationState ConvertContentNavigationEntry(
-    content::NavigationEntry* entry) {
+    content::NavigationEntry* entry,
+    content::WebContents* web_contents) {
   DCHECK(entry);
 
   fuchsia::web::NavigationState converted;
@@ -94,41 +95,13 @@
       converted.set_page_type(fuchsia::web::PageType::ERROR);
       break;
   }
+
+  converted.set_can_go_back(web_contents->GetController().CanGoBack());
+  converted.set_can_go_forward(web_contents->GetController().CanGoForward());
+
   return converted;
 }
 
-// Computes the observable differences between |old_entry| and |new_entry|.
-// Returns true if they are different, |false| if their observable fields are
-// identical.
-bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry,
-                           const fuchsia::web::NavigationState& new_entry,
-                           fuchsia::web::NavigationState* difference) {
-  DCHECK(difference);
-
-  bool is_changed = false;
-
-  DCHECK(new_entry.has_title());
-  if (!old_entry.has_title() || (new_entry.title() != old_entry.title())) {
-    is_changed = true;
-    difference->set_title(new_entry.title());
-  }
-
-  DCHECK(new_entry.has_url());
-  if (!old_entry.has_url() || (new_entry.url() != old_entry.url())) {
-    is_changed = true;
-    difference->set_url(new_entry.url());
-  }
-
-  DCHECK(new_entry.has_page_type());
-  if (!old_entry.has_page_type() ||
-      (new_entry.page_type() != old_entry.page_type())) {
-    is_changed = true;
-    difference->set_page_type(new_entry.page_type());
-  }
-
-  return is_changed;
-}
-
 class FrameFocusRules : public wm::BaseFocusRules {
  public:
   FrameFocusRules() = default;
@@ -496,7 +469,7 @@
 
 void FrameImpl::OnNavigationEntryChanged(content::NavigationEntry* entry) {
   fuchsia::web::NavigationState entry_converted =
-      ConvertContentNavigationEntry(entry);
+      ConvertContentNavigationEntry(entry, web_contents_.get());
   pending_navigation_event_is_dirty_ |= DiffNavigationEntries(
       cached_navigation_state_, entry_converted, &pending_navigation_event_);
   cached_navigation_state_ = std::move(entry_converted);
@@ -609,7 +582,7 @@
     return;
   }
 
-  callback(ConvertContentNavigationEntry(entry));
+  callback(ConvertContentNavigationEntry(entry, web_contents_.get()));
 }
 
 bool FrameImpl::ShouldCreateWebContents(
@@ -712,3 +685,46 @@
 void FrameImpl::TitleWasSet(content::NavigationEntry* entry) {
   OnNavigationEntryChanged(entry);
 }
+
+bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry,
+                           const fuchsia::web::NavigationState& new_entry,
+                           fuchsia::web::NavigationState* difference) {
+  DCHECK(difference);
+
+  bool is_changed = false;
+
+  DCHECK(new_entry.has_title());
+  if (!old_entry.has_title() || (new_entry.title() != old_entry.title())) {
+    is_changed = true;
+    difference->set_title(new_entry.title());
+  }
+
+  DCHECK(new_entry.has_url());
+  if (!old_entry.has_url() || (new_entry.url() != old_entry.url())) {
+    is_changed = true;
+    difference->set_url(new_entry.url());
+  }
+
+  DCHECK(new_entry.has_page_type());
+  if (!old_entry.has_page_type() ||
+      (new_entry.page_type() != old_entry.page_type())) {
+    is_changed = true;
+    difference->set_page_type(new_entry.page_type());
+  }
+
+  DCHECK(new_entry.has_can_go_back());
+  if (!old_entry.has_can_go_back() ||
+      old_entry.can_go_back() != new_entry.can_go_back()) {
+    is_changed = true;
+    difference->set_can_go_back(new_entry.can_go_back());
+  }
+
+  DCHECK(new_entry.has_can_go_forward());
+  if (!old_entry.has_can_go_forward() ||
+      old_entry.can_go_forward() != new_entry.can_go_forward()) {
+    is_changed = true;
+    difference->set_can_go_forward(new_entry.can_go_forward());
+  }
+
+  return is_changed;
+}
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index 5d257e2..85e3f4a 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -180,4 +180,10 @@
   DISALLOW_COPY_AND_ASSIGN(FrameImpl);
 };
 
+// Computes the observable differences between |old_entry| and |new_entry|.
+// Returns true if they are different, |false| if their observable fields are
+// identical.
+bool DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry,
+                           const fuchsia::web::NavigationState& new_entry,
+                           fuchsia::web::NavigationState* difference);
 #endif  // FUCHSIA_ENGINE_BROWSER_FRAME_IMPL_H_
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc
index 41139ae..d75f0a7 100644
--- a/fuchsia/engine/browser/frame_impl_browsertest.cc
+++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -119,6 +119,21 @@
   DISALLOW_COPY_AND_ASSIGN(FrameImplTest);
 };
 
+void VerifyCanGoBackAndForward(fuchsia::web::NavigationController* controller,
+                               bool can_go_back_expected,
+                               bool can_go_forward_expected) {
+  base::RunLoop run_loop;
+  cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> visible_entry(
+      run_loop.QuitClosure());
+  controller->GetVisibleEntry(
+      cr_fuchsia::CallbackToFitFunction(visible_entry.GetReceiveCallback()));
+  run_loop.Run();
+  EXPECT_TRUE(visible_entry->has_can_go_back());
+  EXPECT_EQ(visible_entry->can_go_back(), can_go_back_expected);
+  EXPECT_TRUE(visible_entry->has_can_go_forward());
+  EXPECT_EQ(visible_entry->can_go_forward(), can_go_forward_expected);
+}
+
 // Verifies that the browser will navigate and generate a navigation listener
 // event when LoadUrl() is called.
 IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateFrame) {
@@ -229,25 +244,33 @@
 
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
       &controller, fuchsia::web::LoadUrlParams(), title1.spec()));
-  navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
+  navigation_listener_.RunUntilUrlTitleBackForwardEquals(title1, kPage1Title,
+                                                         false, false);
 
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
       &controller, fuchsia::web::LoadUrlParams(), title2.spec()));
-  navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
+  navigation_listener_.RunUntilUrlTitleBackForwardEquals(title2, kPage2Title,
+                                                         true, false);
 
+  VerifyCanGoBackAndForward(controller.get(), true, false);
   controller->GoBack();
-  navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
+  navigation_listener_.RunUntilUrlTitleBackForwardEquals(title1, kPage1Title,
+                                                         false, true);
 
   // At the top of the navigation entry list; this should be a no-op.
+  VerifyCanGoBackAndForward(controller.get(), false, true);
   controller->GoBack();
 
   // Process the navigation request message.
   base::RunLoop().RunUntilIdle();
 
+  VerifyCanGoBackAndForward(controller.get(), false, true);
   controller->GoForward();
-  navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
+  navigation_listener_.RunUntilUrlTitleBackForwardEquals(title2, kPage2Title,
+                                                         true, false);
 
   // At the end of the navigation entry list; this should be a no-op.
+  VerifyCanGoBackAndForward(controller.get(), true, false);
   controller->GoForward();
 
   // Process the navigation request message.
diff --git a/fuchsia/engine/browser/frame_impl_unittest.cc b/fuchsia/engine/browser/frame_impl_unittest.cc
new file mode 100644
index 0000000..a8a4098
--- /dev/null
+++ b/fuchsia/engine/browser/frame_impl_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia/engine/browser/frame_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using NavigationState = fuchsia::web::NavigationState;
+
+namespace {
+
+const char kUrl1[] = "http://www.url1.com/";
+const char kUrl2[] = "http://www.url2.com/";
+const char kTitle1[] = "title1";
+const char kTitle2[] = "title2";
+
+NavigationState CreateNavigationState(const GURL& url,
+                                      base::StringPiece title,
+                                      fuchsia::web::PageType page_type,
+                                      bool can_go_back,
+                                      bool can_go_forward) {
+  NavigationState navigation_state;
+
+  navigation_state.set_url(url.spec());
+  navigation_state.set_title(title.as_string());
+  navigation_state.set_page_type(fuchsia::web::PageType(page_type));
+  navigation_state.set_can_go_back(can_go_back);
+  navigation_state.set_can_go_forward(can_go_forward);
+
+  return navigation_state;
+}
+
+}  // namespace
+
+// Verifies that two NavigationStates that are the same are differenced
+// correctly.
+TEST(FrameImplUnitTest, DiffNavigationEntriesNoChange) {
+  fuchsia::web::NavigationState difference;
+  NavigationState state = CreateNavigationState(
+      GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, true);
+
+  EXPECT_FALSE(DiffNavigationEntries(state, state, &difference));
+}
+
+// Verifies that states with different URL and title are correctly checked.
+TEST(FrameImplUnitTest, DiffNavigationEntriesTitleUrl) {
+  fuchsia::web::NavigationState difference;
+  NavigationState state1 = CreateNavigationState(
+      GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, true);
+  NavigationState state2 = CreateNavigationState(
+      GURL(kUrl2), kTitle2, fuchsia::web::PageType::NORMAL, true, true);
+
+  bool is_changed = DiffNavigationEntries(state1, state2, &difference);
+
+  EXPECT_TRUE(is_changed);
+  EXPECT_TRUE(difference.has_title());
+  EXPECT_EQ(difference.title(), kTitle2);
+  EXPECT_TRUE(difference.has_url());
+  EXPECT_EQ(difference.url(), kUrl2);
+}
+
+// Verifies that states with different can_go_back and can_go_forward are
+// correctly checked.
+TEST(FrameImplUnitTest, DiffNavigationEntriesGoBackAndForward) {
+  fuchsia::web::NavigationState difference;
+  NavigationState state1 = CreateNavigationState(
+      GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, true, false);
+  NavigationState state2 = CreateNavigationState(
+      GURL(kUrl1), kTitle1, fuchsia::web::PageType::NORMAL, false, true);
+
+  bool is_changed = DiffNavigationEntries(state1, state2, &difference);
+
+  EXPECT_TRUE(difference.has_can_go_back());
+  EXPECT_TRUE(difference.has_can_go_back());
+  EXPECT_TRUE(is_changed);
+  EXPECT_TRUE(difference.can_go_forward());
+  EXPECT_FALSE(difference.can_go_back());
+}
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc
index 35f1ff1..3f3331b 100644
--- a/media/capture/video/chromeos/request_manager.cc
+++ b/media/capture/video/chromeos/request_manager.cc
@@ -700,8 +700,8 @@
         processing_buffer_ids_.erase(*pending_result.input_buffer_id);
 
         // If all reprocess tasks are done for this buffer, release the buffer.
-        if (base::ContainsKey(buffer_id_reprocess_tasks_map_,
-                              *pending_result.input_buffer_id)) {
+        if (!base::ContainsKey(buffer_id_reprocess_tasks_map_,
+                               *pending_result.input_buffer_id)) {
           stream_buffer_manager_->ReleaseBuffer(
               StreamType::kYUVOutput, *pending_result.input_buffer_id);
         }
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 2e9fd58..550b310 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -302,8 +302,6 @@
   // Callback to call in this queue's destructor.
   base::OnceClosure destroy_cb_;
 
-  base::WeakPtrFactory<V4L2Queue> weak_this_factory_;
-
   V4L2Queue(scoped_refptr<V4L2Device> dev,
             enum v4l2_buf_type type,
             base::OnceClosure destroy_cb);
@@ -312,6 +310,9 @@
   friend class base::RefCountedThreadSafe<V4L2Queue>;
 
   SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<V4L2Queue> weak_this_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(V4L2Queue);
 };
 
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
index 74a2c58..0298556 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -1034,6 +1034,15 @@
                      base::Unretained(this), base::Passed(&job_record)));
 }
 
+void V4L2JpegEncodeAccelerator::EncodeWithDmaBuf(
+    scoped_refptr<VideoFrame> input_frame,
+    scoped_refptr<VideoFrame> output_frame,
+    int quality,
+    int32_t buffer_id,
+    const BitstreamBuffer* exif_buffer) {
+  NOTIMPLEMENTED();
+}
+
 void V4L2JpegEncodeAccelerator::EncodeTask(
     std::unique_ptr<JobRecord> job_record) {
   DCHECK(encoder_task_runner_->BelongsToCurrentThread());
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
index 48b9d1f..a380330 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
@@ -60,6 +60,12 @@
               const BitstreamBuffer* exif_buffer,
               const BitstreamBuffer& output_buffer) override;
 
+  void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
+                        scoped_refptr<VideoFrame> output_frame,
+                        int quality,
+                        int32_t buffer_id,
+                        const BitstreamBuffer* exif_buffer) override;
+
  private:
   // Record for input buffers.
   struct I420BufferRecord {
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index 046b9348..26a6060 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -337,4 +337,13 @@
                      base::Unretained(encoder_.get()), std::move(request)));
 }
 
+void VaapiJpegEncodeAccelerator::EncodeWithDmaBuf(
+    scoped_refptr<VideoFrame> input_frame,
+    scoped_refptr<VideoFrame> output_frame,
+    int quality,
+    int32_t buffer_id,
+    const BitstreamBuffer* exif_buffer) {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
index 315b439f..0f94d30 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
@@ -44,6 +44,12 @@
               const BitstreamBuffer* exif_buffer,
               const BitstreamBuffer& output_buffer) override;
 
+  void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
+                        scoped_refptr<VideoFrame> output_frame,
+                        int quality,
+                        int32_t buffer_id,
+                        const BitstreamBuffer* exif_buffer) override;
+
  private:
   // An input video frame and the corresponding output buffer awaiting
   // consumption, provided by the client.
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
index b6b3c8b..9574313 100644
--- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
+++ b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
@@ -4,7 +4,9 @@
 
 #include "media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h"
 
+#include <linux/videodev2.h>
 #include <stdint.h>
+#include <sys/mman.h>
 
 #include <memory>
 #include <utility>
@@ -20,17 +22,73 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/linux/native_pixmap_dmabuf.h"
+
+namespace media {
 
 namespace {
 
-#if defined(OS_CHROMEOS)
 const int kJpegQuality = 90;
-#endif
+
+scoped_refptr<media::VideoFrame> ConstructVideoFrame(
+    std::vector<chromeos_camera::mojom::DmaBufPlanePtr> dma_buf_planes,
+    VideoPixelFormat pixel_format,
+    int32_t width,
+    int32_t height) {
+  size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
+  if (num_planes != dma_buf_planes.size()) {
+    DLOG(ERROR) << "The amount of DMA buf planes does not match the format.";
+    return nullptr;
+  }
+  if (width <= 0 || height <= 0) {
+    DLOG(ERROR) << "Width and height should > 0: " << width << ", " << height;
+    return nullptr;
+  }
+  gfx::Size coded_size(width, height);
+  gfx::Rect visible_rect(coded_size);
+
+  std::vector<base::ScopedFD> dma_buf_fds(num_planes);
+  std::vector<size_t> buffer_sizes(num_planes);
+  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+
+  for (size_t i = 0; i < num_planes; ++i) {
+    dma_buf_fds[i] =
+        mojo::UnwrapPlatformHandle(std::move(dma_buf_planes[i]->fd_handle))
+            .TakeFD();
+    planes[i].stride = dma_buf_planes[i]->stride;
+    planes[i].offset = dma_buf_planes[i]->offset;
+    buffer_sizes[i] = dma_buf_planes[i]->size;
+  }
+  auto layout = VideoFrameLayout::CreateWithPlanes(
+      pixel_format, coded_size, std::move(planes), std::move(buffer_sizes));
+
+  return VideoFrame::WrapExternalDmabufs(*layout,       // layout
+                                         visible_rect,  // visible_rect
+                                         coded_size,    // natural_size
+                                         std::move(dma_buf_fds),  // dmabuf_fds
+                                         base::TimeDelta());      // timestamp
+}
+
+VideoPixelFormat ToVideoPixelFormat(uint32_t fourcc_fmt) {
+  switch (fourcc_fmt) {
+    case V4L2_PIX_FMT_NV12:
+    case V4L2_PIX_FMT_NV12M:
+      return PIXEL_FORMAT_NV12;
+
+    case V4L2_PIX_FMT_YUV420:
+    case V4L2_PIX_FMT_YUV420M:
+      return PIXEL_FORMAT_I420;
+
+    case V4L2_PIX_FMT_RGB32:
+      return PIXEL_FORMAT_ARGB;
+
+    default:
+      return PIXEL_FORMAT_UNKNOWN;
+  }
+}
 
 }  // namespace
 
-namespace media {
-
 // static
 void CrOSMojoJpegEncodeAcceleratorService::Create(
     chromeos_camera::mojom::JpegEncodeAcceleratorRequest request) {
@@ -101,7 +159,6 @@
     mojo::ScopedHandle output_handle,
     uint32_t output_buffer_size,
     EncodeWithFDCallback callback) {
-#if defined(OS_CHROMEOS)
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   base::PlatformFile input_fd;
   base::PlatformFile exif_fd;
@@ -158,7 +215,14 @@
     mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_");
     return;
   }
-  encode_cb_map_.emplace(buffer_id, std::move(callback));
+  auto wrapped_callback = base::BindOnce(
+      [](int32_t buffer_id, EncodeWithFDCallback callback,
+         uint32_t encoded_picture_size,
+         media::JpegEncodeAccelerator::Status error) {
+        std::move(callback).Run(buffer_id, encoded_picture_size, error);
+      },
+      buffer_id, std::move(callback));
+  encode_cb_map_.emplace(buffer_id, std::move(wrapped_callback));
 
   auto input_shm = std::make_unique<base::SharedMemory>(input_shm_handle, true);
   if (!input_shm->Map(input_buffer_size)) {
@@ -193,9 +257,66 @@
 
   DCHECK(accelerator_);
   accelerator_->Encode(frame, kJpegQuality, exif_buffer.get(), output_buffer);
-#else
-  NOTREACHED();
-#endif
+}
+
+void CrOSMojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
+    int32_t buffer_id,
+    uint32_t input_format,
+    std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes,
+    std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes,
+    mojo::ScopedHandle exif_handle,
+    uint32_t exif_buffer_size,
+    int32_t coded_size_width,
+    int32_t coded_size_height,
+    EncodeWithDmaBufCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (coded_size_width <= 0 || coded_size_height <= 0) {
+    std::move(callback).Run(
+        0, ::media::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
+    return;
+  }
+  if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) {
+    mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_");
+    return;
+  }
+
+  base::PlatformFile exif_fd;
+  auto result = mojo::UnwrapPlatformFile(std::move(exif_handle), &exif_fd);
+  if (result != MOJO_RESULT_OK) {
+    std::move(callback).Run(
+        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+    return;
+  }
+
+  auto input_video_frame = ConstructVideoFrame(
+      std::move(input_planes), ToVideoPixelFormat(input_format),
+      coded_size_width, coded_size_height);
+  if (!input_video_frame) {
+    std::move(callback).Run(
+        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+    return;
+  }
+  auto output_video_frame =
+      ConstructVideoFrame(std::move(output_planes), PIXEL_FORMAT_MJPEG,
+                          coded_size_width, coded_size_height);
+  if (!output_video_frame) {
+    std::move(callback).Run(
+        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+    return;
+  }
+  base::UnguessableToken exif_guid = base::UnguessableToken::Create();
+  base::SharedMemoryHandle exif_shm_handle(base::FileDescriptor(exif_fd, true),
+                                           0u, exif_guid);
+  std::unique_ptr<media::BitstreamBuffer> exif_buffer;
+  if (exif_buffer_size > 0) {
+    exif_buffer = std::make_unique<media::BitstreamBuffer>(
+        buffer_id, exif_shm_handle, exif_buffer_size);
+  }
+  encode_cb_map_.emplace(buffer_id, std::move(callback));
+
+  DCHECK(accelerator_);
+  accelerator_->EncodeWithDmaBuf(input_video_frame, output_video_frame,
+                                 kJpegQuality, buffer_id, exif_buffer.get());
 }
 
 void CrOSMojoJpegEncodeAcceleratorService::NotifyEncodeStatus(
@@ -206,9 +327,9 @@
 
   auto iter = encode_cb_map_.find(bitstream_buffer_id);
   DCHECK(iter != encode_cb_map_.end());
-  EncodeWithFDCallback encode_cb = std::move(iter->second);
+  EncodeWithDmaBufCallback encode_cb = std::move(iter->second);
   encode_cb_map_.erase(iter);
-  std::move(encode_cb).Run(bitstream_buffer_id, encoded_picture_size, error);
+  std::move(encode_cb).Run(encoded_picture_size, error);
 }
 
 }  // namespace media
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
index 049eb39..4374eaa 100644
--- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
+++ b/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
@@ -36,7 +36,8 @@
                    ::media::JpegEncodeAccelerator::Status status) override;
 
  private:
-  using EncodeCallbackMap = std::unordered_map<int32_t, EncodeWithFDCallback>;
+  using EncodeCallbackMap =
+      std::unordered_map<int32_t, EncodeWithDmaBufCallback>;
 
   // This constructor internally calls
   // GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories() to
@@ -45,6 +46,8 @@
 
   // chromeos_camera::mojom::JpegEncodeAccelerator implementation.
   void Initialize(InitializeCallback callback) override;
+
+  // TODO(wtlee): To be deprecated. (crbug.com/944705)
   void EncodeWithFD(int32_t buffer_id,
                     mojo::ScopedHandle input_fd,
                     uint32_t input_buffer_size,
@@ -56,6 +59,17 @@
                     uint32_t output_buffer_size,
                     EncodeWithFDCallback callback) override;
 
+  void EncodeWithDmaBuf(
+      int32_t buffer_id,
+      uint32_t input_format,
+      std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes,
+      std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes,
+      mojo::ScopedHandle exif_handle,
+      uint32_t exif_buffer_size,
+      int32_t coded_size_width,
+      int32_t coded_size_height,
+      EncodeWithDmaBufCallback callback) override;
+
   void NotifyEncodeStatus(int32_t bitstream_buffer_id,
                           size_t encoded_picture_size,
                           ::media::JpegEncodeAccelerator::Status status);
diff --git a/media/video/jpeg_encode_accelerator.h b/media/video/jpeg_encode_accelerator.h
index 6653a03..9c2b289 100644
--- a/media/video/jpeg_encode_accelerator.h
+++ b/media/video/jpeg_encode_accelerator.h
@@ -104,6 +104,23 @@
                       int quality,
                       const BitstreamBuffer* exif_buffer,
                       const BitstreamBuffer& output_buffer) = 0;
+
+  // Encodes the given |video_frame| that contains a YUV image. Client will
+  // receive the encoded result in Client::VideoFrameReady() callback with the
+  // corresponding |output_buffer.id()|, or receive
+  // Client::NotifyError() callback.
+  // Parameters:
+  //  |input_frame| contains the YUV image to be encoded.
+  //  |output_frame| is used to represent the output Dma-buf layout.
+  //  |quality| of JPEG image. The range is from 1~100. High value means high
+  //  quality.
+  //  |exif_buffer| contains Exif data to be inserted into JPEG image. If it's
+  //  nullptr, the JFIF APP0 segment will be inserted.
+  virtual void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
+                                scoped_refptr<VideoFrame> output_frame,
+                                int quality,
+                                int32_t buffer_id,
+                                const BitstreamBuffer* exif_buffer) = 0;
 };
 
 }  // namespace media
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 099162e..a9e82c7 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -101,6 +101,38 @@
                             NUM_LOCATIONS);
 }
 
+void RecordConnectionCloseErrorCode(quic::QuicErrorCode error,
+                                    quic::ConnectionCloseSource source,
+                                    const std::string& hostname,
+                                    bool handshake_confirmed) {
+  bool is_google_host = HasGoogleHost(GURL("https://" + hostname));
+  std::string histogram = "Net.QuicSession.ConnectionCloseErrorCode";
+
+  if (source == quic::ConnectionCloseSource::FROM_PEER) {
+    histogram += "Server";
+  } else {
+    histogram += "Client";
+  }
+  base::UmaHistogramSparse(histogram, error);
+
+  if (handshake_confirmed) {
+    base::UmaHistogramSparse(histogram + ".HandshakeConfirmed", error);
+  } else {
+    base::UmaHistogramSparse(histogram + ".HandshakeNotConfirmed", error);
+  }
+
+  if (is_google_host) {
+    histogram += "Google";
+    base::UmaHistogramSparse(histogram, error);
+
+    if (handshake_confirmed) {
+      base::UmaHistogramSparse(histogram + ".HandshakeConfirmed", error);
+    } else {
+      base::UmaHistogramSparse(histogram + ".HandshakeNotConfirmed", error);
+    }
+  }
+}
+
 NetLogParametersCallback NetLogQuicConnectionMigrationTriggerCallback(
     const char* trigger) {
   return NetLog::StringCallback("trigger", trigger);
@@ -1556,7 +1588,9 @@
     quic::ConnectionCloseSource source) {
   DCHECK(!connection()->connected());
   logger_->OnConnectionClosed(error, error_details, source);
-  bool is_google_host = HasGoogleHost(GURL("https://" + session_key_.host()));
+
+  RecordConnectionCloseErrorCode(error, source, session_key_.host(),
+                                 IsCryptoHandshakeConfirmed());
   if (source == quic::ConnectionCloseSource::FROM_PEER) {
     if (error == quic::QUIC_PUBLIC_RESET) {
       // is_from_google_server will be true if the received EPID is
@@ -1581,15 +1615,6 @@
       }
     }
     if (IsCryptoHandshakeConfirmed()) {
-      if (is_google_host) {
-        base::UmaHistogramSparse(
-            "Net.QuicSession.ConnectionCloseErrorCodeServerGoogle."
-            "HandshakeConfirmed",
-            error);
-      }
-      base::UmaHistogramSparse(
-          "Net.QuicSession.ConnectionCloseErrorCodeServer.HandshakeConfirmed",
-          error);
       base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
           "Net.QuicSession.StreamCloseErrorCodeServer.HandshakeConfirmed",
           base::HistogramBase::kUmaTargetedHistogramFlag);
@@ -1597,23 +1622,8 @@
       if (num_streams > 0)
         histogram->AddCount(error, num_streams);
     }
-    if (is_google_host) {
-      base::UmaHistogramSparse(
-          "Net.QuicSession.ConnectionCloseErrorCodeServerGoogle", error);
-    }
-    base::UmaHistogramSparse("Net.QuicSession.ConnectionCloseErrorCodeServer",
-                             error);
   } else {
     if (IsCryptoHandshakeConfirmed()) {
-      if (is_google_host) {
-        base::UmaHistogramSparse(
-            "Net.QuicSession.ConnectionCloseErrorCodeClientGoogle."
-            "HandshakeConfirmed",
-            error);
-      }
-      base::UmaHistogramSparse(
-          "Net.QuicSession.ConnectionCloseErrorCodeClient.HandshakeConfirmed",
-          error);
       base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
           "Net.QuicSession.StreamCloseErrorCodeClient.HandshakeConfirmed",
           base::HistogramBase::kUmaTargetedHistogramFlag);
@@ -1627,12 +1637,6 @@
             connection()->IsPathDegrading());
       }
     }
-    if (is_google_host) {
-      base::UmaHistogramSparse(
-          "Net.QuicSession.ConnectionCloseErrorCodeClientGoogle", error);
-    }
-    base::UmaHistogramSparse("Net.QuicSession.ConnectionCloseErrorCodeClient",
-                             error);
     if (error == quic::QUIC_TOO_MANY_RTOS) {
       UMA_HISTOGRAM_COUNTS_1000(
           "Net.QuicSession.ClosedByRtoAtClient.ReceivedPacketCount",
diff --git a/services/device/geolocation/network_location_request.cc b/services/device/geolocation/network_location_request.cc
index 4497a19e..3b0c65d 100644
--- a/services/device/geolocation/network_location_request.cc
+++ b/services/device/geolocation/network_location_request.cc
@@ -277,8 +277,10 @@
   auto wifi_access_point_list = std::make_unique<base::ListValue>();
   for (auto* ap_data : access_points_by_signal_strength) {
     auto wifi_dict = std::make_unique<base::DictionaryValue>();
-    AddString("macAddress", base::UTF16ToUTF8(ap_data->mac_address),
-              wifi_dict.get());
+    auto macAddress = base::UTF16ToUTF8(ap_data->mac_address);
+    if (macAddress.empty())
+      continue;
+    AddString("macAddress", macAddress, wifi_dict.get());
     AddInteger("signalStrength", ap_data->radio_signal_strength,
                wifi_dict.get());
     AddInteger("age", age_milliseconds, wifi_dict.get());
diff --git a/services/service_manager/sandbox/mac/common.sb b/services/service_manager/sandbox/mac/common.sb
index c0db1ebd..f786184 100644
--- a/services/service_manager/sandbox/mac/common.sb
+++ b/services/service_manager/sandbox/mac/common.sb
@@ -182,7 +182,6 @@
   (sysctl-name "hw.cachelinesize_compat")
   (sysctl-name "hw.cpufrequency_compat")
   (sysctl-name "hw.cputype")
-  (sysctl-name "hw.logicalcpu_max")
   (sysctl-name "hw.machine")
   (sysctl-name "hw.ncpu")
   (sysctl-name "hw.pagesize_compat")
@@ -193,7 +192,6 @@
   (sysctl-name "kern.maxfilesperproc")
   (sysctl-name "kern.osrelease")
   (sysctl-name "kern.ostype")
-  (sysctl-name "kern.osvariant_status")
   (sysctl-name "kern.osversion")
   (sysctl-name "kern.usrstack64")
   (sysctl-name "kern.version")
diff --git a/services/service_manager/sandbox/mac/renderer.sb b/services/service_manager/sandbox/mac/renderer.sb
index c54fd4c..6e1426d 100644
--- a/services/service_manager/sandbox/mac/renderer.sb
+++ b/services/service_manager/sandbox/mac/renderer.sb
@@ -4,17 +4,9 @@
 
 ; --- The contents of common.sb implicitly included here. ---
 
-; Put the denials first.
-; crbug.com/799149: These operations are allowed by default.
-(deny iokit-get-properties process-info* nvram*)
-
 ; Allow cf prefs to work.
 (allow user-preference-read)
 
-; process-info
-(allow process-info-pidinfo)
-(allow process-info-setcontrol (target self))
-
 ; File reads.
 ; Reads from the home directory.
 (allow file-read-data
@@ -75,15 +67,3 @@
   (global-name "com.apple.lsd.mapdb")
   (global-name "com.apple.system.notification_center")  ; https://crbug.com/792217
 )
-
-; IOKit properties.
-(allow iokit-get-properties
-  (iokit-property "CaseSensitive")
-  (iokit-property "Ejectable")
-  (iokit-property "Encrypted")
-  (iokit-property "IOClassNameOverride")
-  (iokit-property "IOMediaIcon")
-  (iokit-property "Protocol Characteristics")
-  (iokit-property "Removable")
-  (iokit-property "image-encrypted")
-)
diff --git a/storage/browser/fileapi/obfuscated_file_util.cc b/storage/browser/fileapi/obfuscated_file_util.cc
index be96319f..949377c 100644
--- a/storage/browser/fileapi/obfuscated_file_util.cc
+++ b/storage/browser/fileapi/obfuscated_file_util.cc
@@ -213,9 +213,12 @@
     : public ObfuscatedFileUtil::AbstractOriginEnumerator {
  public:
   using OriginRecord = SandboxOriginDatabase::OriginRecord;
-  ObfuscatedOriginEnumerator(SandboxOriginDatabaseInterface* origin_database,
-                             const base::FilePath& base_file_path)
-      : base_file_path_(base_file_path) {
+  ObfuscatedOriginEnumerator(
+      SandboxOriginDatabaseInterface* origin_database,
+      base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
+      const base::FilePath& base_file_path)
+      : base_file_path_(base_file_path),
+        memory_file_util_(std::move(memory_file_util)) {
     if (origin_database)
       origin_database->ListAllOrigins(&origins_);
   }
@@ -243,13 +246,17 @@
     }
     base::FilePath path =
         base_file_path_.Append(current_.path).AppendASCII(type_string);
-    return base::DirectoryExists(path);
+    if (memory_file_util_)
+      return memory_file_util_->DirectoryExists(path);
+    else
+      return base::DirectoryExists(path);
   }
 
  private:
   std::vector<OriginRecord> origins_;
   OriginRecord current_;
   base::FilePath base_file_path_;
+  base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
 };
 
 ObfuscatedFileUtil::ObfuscatedFileUtil(
@@ -947,8 +954,15 @@
   std::vector<SandboxOriginDatabase::OriginRecord> origins;
 
   InitOriginDatabase(GURL(), false);
-  return std::make_unique<ObfuscatedOriginEnumerator>(origin_database_.get(),
-                                                      file_system_directory_);
+  base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> file_util_delegate;
+  if (is_incognito() &&
+      base::FeatureList::IsEnabled(features::kEnableFilesystemInIncognito)) {
+    file_util_delegate =
+        static_cast<ObfuscatedFileUtilMemoryDelegate*>(delegate())
+            ->GetWeakPtr();
+  }
+  return std::make_unique<ObfuscatedOriginEnumerator>(
+      origin_database_.get(), file_util_delegate, file_system_directory_);
 }
 
 void ObfuscatedFileUtil::DestroyDirectoryDatabase(
diff --git a/storage/browser/fileapi/obfuscated_file_util_unittest.cc b/storage/browser/fileapi/obfuscated_file_util_unittest.cc
index 51a0d10..c2bd85b 100644
--- a/storage/browser/fileapi/obfuscated_file_util_unittest.cc
+++ b/storage/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -225,27 +225,13 @@
 
   void TearDown() override {
     if (in_memory_test())
-      CheckFilesInFileSystemDirectory();
+      ASSERT_TRUE(IsDirectoryEmpty(data_dir_.GetPath()));
 
     quota_manager_ = nullptr;
     scoped_task_environment_.RunUntilIdle();
     sandbox_file_system_.TearDown();
   }
 
-  void CheckFilesInFileSystemDirectory() {
-    // Make sure there is no file on disk for in memory file system. Ignore
-    // directories created by the ObfuscatedFileUtil as they do not represent
-    // user directories.
-    // TODO(https://crbug.com/93417): Investigate why directories are created.
-    std::unique_ptr<storage::FileSystemFileUtil::AbstractFileEnumerator>
-        enumerator = storage::NativeFileUtil::CreateFileEnumerator(
-            data_dir_.GetPath(), true);
-    for (base::FilePath path = enumerator->Next(); !path.empty();
-         path = enumerator->Next()) {
-      ASSERT_TRUE(storage::NativeFileUtil::DirectoryExists(path));
-    }
-  }
-
   std::unique_ptr<FileSystemOperationContext> LimitedContext(
       int64_t allowed_bytes_growth) {
     std::unique_ptr<FileSystemOperationContext> context(
diff --git a/storage/browser/test/sandbox_file_system_test_helper.cc b/storage/browser/test/sandbox_file_system_test_helper.cc
index c353dd69..0646423 100644
--- a/storage/browser/test/sandbox_file_system_test_helper.cc
+++ b/storage/browser/test/sandbox_file_system_test_helper.cc
@@ -165,9 +165,6 @@
   file_system_context_->sandbox_delegate()->
       GetBaseDirectoryForOriginAndType(origin_, type_, true /* create */);
 
-  // Initialize the usage cache file. The directory does not exist and should be
-  // created for in memory tests.
-  base::CreateDirectory(GetUsageCachePath().DirName());
   base::FilePath usage_cache_path = GetUsageCachePath();
   if (!usage_cache_path.empty())
     usage_cache()->UpdateUsage(usage_cache_path, 0);
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 0d1f300..e729445 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4962,6 +4962,18 @@
         }
       },
       {
+        "isolate_name": "content_shell_crash_test",
+        "name": "content_shell_crash_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        }
+      },
+      {
         "isolate_name": "devtools_closure_compile",
         "name": "devtools_closure_compile",
         "swarming": {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 976158d..42772661 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -433,10 +433,6 @@
     },
   },
   'content_shell_crash_test': {
-    'remove_from': [
-      # chromium.linux
-      'Linux Tests (dbg)(1)(32)',  # https://crbug.com/859264
-    ],
     'modifications': {
       # chromium.win
       'Win10 Tests x64 (dbg)': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1bce82c..9718179d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5007,6 +5007,26 @@
             ]
         }
     ],
+    "SyncUSSAutofillWalletMetadata": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SyncUSSAutofillWalletMetadata"
+                    ]
+                }
+            ]
+        }
+    ],
     "SyncUssBookmarks'": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/clipboard/clipboard.mojom b/third_party/blink/public/mojom/clipboard/clipboard.mojom
index 669cee5..0b38cb7 100644
--- a/third_party/blink/public/mojom/clipboard/clipboard.mojom
+++ b/third_party/blink/public/mojom/clipboard/clipboard.mojom
@@ -55,34 +55,28 @@
   ReadCustomData(ClipboardBuffer buffer, mojo_base.mojom.String16 type) =>
       (mojo_base.mojom.BigString16 result);
 
-  // Writing to the clipboard via IPC is a two-phase operation. First, the
+  // Writing to the clipboard via mojo is a two-phase operation. First, the
   // sender sends the different types of data it'd like to write to the
   // receiver. Then, it sends a commit message to commit the data to the system
   // clipboard.
-  // TODO(dcheng): Remove |buffer| parameters from Write* functions.
-  WriteText(ClipboardBuffer buffer, mojo_base.mojom.BigString16 text);
+  WriteText(mojo_base.mojom.BigString16 text);
 
-  WriteHtml(ClipboardBuffer buffer,
-            mojo_base.mojom.BigString16 markup,
-            url.mojom.Url url);
+  WriteHtml(mojo_base.mojom.BigString16 markup, url.mojom.Url url);
 
-  WriteSmartPasteMarker(ClipboardBuffer buffer);
+  WriteSmartPasteMarker();
 
-  WriteCustomData(
-      ClipboardBuffer buffer,
-      map<mojo_base.mojom.String16, mojo_base.mojom.BigString16> data);
+  WriteCustomData(map<mojo_base.mojom.String16, mojo_base.mojom.BigString16> data);
 
   // TODO(dcheng): The |url| parameter should really be a GURL, but <canvas>'s
   // copy as image tries to set very long data: URLs on the clipboard. Using
   // GURL causes the browser to kill the renderer for sending a bad IPC (GURLs
   // bigger than 2 megabytes are considered to be bad). https://crbug.com/459822
-  WriteBookmark(ClipboardBuffer buffer,
-                string url,
+  WriteBookmark(string url,
                 mojo_base.mojom.String16 title);
 
-  WriteImage(ClipboardBuffer buffer, skia.mojom.Bitmap image);
+  WriteImage(skia.mojom.Bitmap image);
 
-  CommitWrite(ClipboardBuffer buffer);
+  CommitWrite();
 
   [EnableIf=is_mac]
   WriteStringToFindPboard(mojo_base.mojom.String16 text);
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
index 88aba4b..9865c84 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -101,7 +101,7 @@
 #if defined(OS_WIN)
   ReplaceNewlinesWithWindowsStyleNewlines(text);
 #endif
-  clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, NonNullString(text));
+  clipboard_->WriteText(NonNullString(text));
 }
 
 String SystemClipboard::ReadHTML(KURL& url,
@@ -131,11 +131,10 @@
 #endif
   ReplaceNBSPWithSpace(text);
 
-  clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard,
-                        NonNullString(markup), document_url);
-  clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, NonNullString(text));
+  clipboard_->WriteHtml(NonNullString(markup), document_url);
+  clipboard_->WriteText(NonNullString(text));
   if (smart_replace_option == kCanSmartReplace)
-    clipboard_->WriteSmartPasteMarker(mojom::ClipboardBuffer::kStandard);
+    clipboard_->WriteSmartPasteMarker();
 }
 
 String SystemClipboard::ReadRTF() {
@@ -161,7 +160,7 @@
   SkBitmap bitmap;
   if (sk_sp<SkImage> sk_image = paint_image.GetSkImage())
     sk_image->asLegacyBitmap(&bitmap);
-  clipboard_->WriteImage(mojom::ClipboardBuffer::kStandard, bitmap);
+  clipboard_->WriteImage(bitmap);
 
   if (url.IsValid() && !url.IsEmpty()) {
 #if !defined(OS_MACOSX)
@@ -169,21 +168,19 @@
     // consistency between platforms, and to help fix errors in applications
     // which prefer text/plain content over image content for compatibility with
     // Microsoft Word.
-    clipboard_->WriteBookmark(mojom::ClipboardBuffer::kStandard,
-                              url.GetString(), NonNullString(title));
+    clipboard_->WriteBookmark(url.GetString(), NonNullString(title));
 #endif
 
     // When writing the image, we also write the image markup so that pasting
     // into rich text editors, such as Gmail, reveals the image. We also don't
     // want to call writeText(), since some applications (WordPad) don't pick
     // the image if there is also a text format on the clipboard.
-    clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard,
-                          URLToImageMarkup(url, title), KURL());
+    clipboard_->WriteHtml(URLToImageMarkup(url, title), KURL());
   }
 }
 
 void SystemClipboard::WriteImage(const SkBitmap& bitmap) {
-  clipboard_->WriteImage(mojom::ClipboardBuffer::kStandard, bitmap);
+  clipboard_->WriteImage(bitmap);
 }
 
 String SystemClipboard::ReadCustomData(const String& type) {
@@ -211,24 +208,21 @@
   for (const WebDragData::Item& item : data.Items()) {
     if (item.storage_type == WebDragData::Item::kStorageTypeString) {
       if (item.string_type == blink::kMimeTypeTextPlain) {
-        clipboard_->WriteText(mojom::ClipboardBuffer::kStandard,
-                              NonNullString(item.string_data));
+        clipboard_->WriteText(NonNullString(item.string_data));
       } else if (item.string_type == blink::kMimeTypeTextHTML) {
-        clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard,
-                              NonNullString(item.string_data), KURL());
+        clipboard_->WriteHtml(NonNullString(item.string_data), KURL());
       } else if (item.string_type != blink::kMimeTypeDownloadURL) {
         custom_data.insert(item.string_type, NonNullString(item.string_data));
       }
     }
   }
   if (!custom_data.IsEmpty()) {
-    clipboard_->WriteCustomData(mojom::ClipboardBuffer::kStandard,
-                                std::move(custom_data));
+    clipboard_->WriteCustomData(std::move(custom_data));
   }
 }
 
 void SystemClipboard::CommitWrite() {
-  clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard);
+  clipboard_->CommitWrite();
 }
 
 bool SystemClipboard::IsValidBufferType(mojom::ClipboardBuffer buffer) {
diff --git a/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc b/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc
index d6e4dec..79cca60 100644
--- a/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc
+++ b/third_party/blink/renderer/core/dom/whitespace_attacher_test.cc
@@ -21,7 +21,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* div = GetDocument().getElementById("block");
-  Text* text = ToText(div->nextSibling());
+  auto* text = To<Text>(div->nextSibling());
   EXPECT_FALSE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -41,7 +41,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* span = GetDocument().getElementById("inline");
-  Text* text = ToText(span->nextSibling());
+  auto* text = To<Text>(span->nextSibling());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -61,9 +61,9 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* span = GetDocument().getElementById("inline");
-  Text* first_whitespace = ToText(span->nextSibling());
-  Text* second_whitespace =
-      ToText(first_whitespace->nextSibling()->nextSibling());
+  auto* first_whitespace = To<Text>(span->nextSibling());
+  auto* second_whitespace =
+      To<Text>(first_whitespace->nextSibling()->nextSibling());
   EXPECT_TRUE(first_whitespace->GetLayoutObject());
   EXPECT_FALSE(second_whitespace->GetLayoutObject());
 
@@ -87,7 +87,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* div = GetDocument().getElementById("block");
-  Text* text = ToText(div->nextSibling());
+  auto* text = To<Text>(div->nextSibling());
   EXPECT_FALSE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -105,7 +105,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* span = GetDocument().getElementById("inline");
-  Text* text = ToText(span->nextSibling());
+  auto* text = To<Text>(span->nextSibling());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -125,8 +125,8 @@
   GetDocument().body()->SetInnerHTMLFromString("Text<!-- --> ");
   UpdateAllLifecyclePhasesForTest();
 
-  Text* text = ToText(GetDocument().body()->firstChild());
-  Text* whitespace = ToText(text->nextSibling()->nextSibling());
+  auto* text = To<Text>(GetDocument().body()->firstChild());
+  auto* whitespace = To<Text>(text->nextSibling()->nextSibling());
   EXPECT_TRUE(text->GetLayoutObject());
   EXPECT_TRUE(whitespace->GetLayoutObject());
 
@@ -149,7 +149,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* div = GetDocument().getElementById("block");
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
   EXPECT_FALSE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -171,7 +171,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* span = GetDocument().getElementById("inline");
-  Text* text = ToText(span->firstChild());
+  auto* text = To<Text>(span->firstChild());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -198,7 +198,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* div = shadow_root.getElementById("block");
-  Text* text = ToText(host->firstChild());
+  auto* text = To<Text>(host->firstChild());
   EXPECT_FALSE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -226,7 +226,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   Element* span = shadow_root.getElementById("inline");
-  Text* text = ToText(host->firstChild());
+  auto* text = To<Text>(host->firstChild());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -250,7 +250,7 @@
 
   Element* div = GetDocument().getElementById("block");
   Element* contents = ToElement(div->nextSibling());
-  Text* text = ToText(contents->firstChild());
+  auto* text = To<Text>(contents->firstChild());
   EXPECT_FALSE(contents->GetLayoutObject());
   EXPECT_FALSE(text->GetLayoutObject());
 
@@ -276,7 +276,7 @@
 
   Element* span = GetDocument().getElementById("inline");
   Element* contents = ToElement(span->nextSibling());
-  Text* text = ToText(contents->firstChild());
+  auto* text = To<Text>(contents->firstChild());
   EXPECT_FALSE(contents->GetLayoutObject());
   EXPECT_TRUE(text->GetLayoutObject());
 
@@ -301,7 +301,7 @@
 
   Element* div = GetDocument().getElementById("block");
   Element* contents = ToElement(div->nextSibling());
-  Text* text = ToText(contents->nextSibling());
+  auto* text = To<Text>(contents->nextSibling());
   EXPECT_FALSE(contents->GetLayoutObject());
   EXPECT_FALSE(text->GetLayoutObject());
 
@@ -329,7 +329,7 @@
 
   Element* div = GetDocument().getElementById("block");
   Element* contents = ToElement(div->nextSibling());
-  Text* text = ToText(contents->nextSibling());
+  auto* text = To<Text>(contents->nextSibling());
   EXPECT_FALSE(contents->GetLayoutObject());
   EXPECT_FALSE(text->GetLayoutObject());
 
@@ -357,7 +357,7 @@
 
   Element* span = GetDocument().getElementById("inline");
   Element* contents = ToElement(span->nextSibling());
-  Text* text = ToText(GetDocument().getElementById("inner")->firstChild());
+  auto* text = To<Text>(GetDocument().getElementById("inner")->firstChild());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -385,7 +385,7 @@
   Element* first_contents = ToElement(span->nextSibling());
   Element* second_contents = ToElement(first_contents->nextSibling());
   Element* last_contents = ToElement(second_contents->nextSibling());
-  Text* text = ToText(last_contents->firstChild());
+  auto* text = To<Text>(last_contents->firstChild());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
@@ -417,7 +417,7 @@
 
   Element* span = shadow_root.getElementById("inline");
   Element* contents = ToElement(span->nextSibling());
-  Text* text = ToText(host->firstChild());
+  auto* text = To<Text>(host->firstChild());
   EXPECT_TRUE(text->GetLayoutObject());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
diff --git a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
index 20fa4bd..a8e4c108 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
@@ -219,15 +219,15 @@
 }
 
 static bool IsNewLineAtPosition(const Position& position) {
-  Node* text_node = position.ComputeContainerNode();
+  auto* text_node = DynamicTo<Text>(position.ComputeContainerNode());
   int offset = position.OffsetInContainerNode();
-  if (!text_node || !text_node->IsTextNode() || offset < 0 ||
-      offset >= static_cast<int>(ToText(text_node)->length()))
+  if (!text_node || offset < 0 ||
+      offset >= static_cast<int>(text_node->length()))
     return false;
 
   DummyExceptionStateForTesting exception_state;
   String text_at_position =
-      ToText(text_node)->substringData(offset, 1, exception_state);
+      text_node->substringData(offset, 1, exception_state);
   if (exception_state.HadException())
     return false;
 
@@ -276,7 +276,7 @@
     if (!start_style->CollapseWhiteSpace() &&
         start.OffsetInContainerNode() > 0) {
       int start_offset = start.OffsetInContainerNode();
-      Text* start_text = ToText(start.ComputeContainerNode());
+      auto* start_text = To<Text>(start.ComputeContainerNode());
       SplitTextNode(start_text, start_offset);
       GetDocument().UpdateStyleAndLayoutTree();
 
@@ -301,7 +301,7 @@
     // Include \n at the end of line if we're at an empty paragraph
     if (end_style->PreserveNewline() && start == end &&
         end.OffsetInContainerNode() <
-            static_cast<int>(ToText(end.ComputeContainerNode())->length())) {
+            static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) {
       int end_offset = end.OffsetInContainerNode();
       // TODO(yosin) We should use |PositionMoveType::CodePoint| for
       // |previousPositionOf()|.
@@ -319,8 +319,8 @@
     if (end_style->UserModify() != EUserModify::kReadOnly &&
         !end_style->CollapseWhiteSpace() && end.OffsetInContainerNode() &&
         end.OffsetInContainerNode() <
-            static_cast<int>(ToText(end.ComputeContainerNode())->length())) {
-      Text* end_container = ToText(end.ComputeContainerNode());
+            static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) {
+      auto* end_container = To<Text>(end.ComputeContainerNode());
       SplitTextNode(end_container, end.OffsetInContainerNode());
       GetDocument().UpdateStyleAndLayoutTree();
 
@@ -361,8 +361,8 @@
   if (!style)
     return end_of_next_paragraph;
 
-  Text* const end_of_next_paragraph_text =
-      ToText(end_of_next_paragraph_position.ComputeContainerNode());
+  auto* const end_of_next_paragraph_text =
+      To<Text>(end_of_next_paragraph_position.ComputeContainerNode());
   if (!style->PreserveNewline() ||
       !end_of_next_paragraph_position.OffsetInContainerNode() ||
       !IsNewLineAtPosition(
@@ -376,10 +376,7 @@
   SplitTextNode(end_of_next_paragraph_text, 1);
   GetDocument().UpdateStyleAndLayout();
   Text* const previous_text =
-      end_of_next_paragraph_text->previousSibling() &&
-              end_of_next_paragraph_text->previousSibling()->IsTextNode()
-          ? ToText(end_of_next_paragraph_text->previousSibling())
-          : nullptr;
+      DynamicTo<Text>(end_of_next_paragraph_text->previousSibling());
   if (end_of_next_paragraph_text == start.ComputeContainerNode() &&
       previous_text) {
     DCHECK_LT(start.OffsetInContainerNode(),
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
index 7a4c5575..9959418 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -1446,10 +1446,10 @@
   // Position("world", 0) instead.
   const unsigned push_down_start_offset =
       push_down_start.ComputeOffsetInContainerNode();
-  Node* push_down_start_container = push_down_start.ComputeContainerNode();
-  if (push_down_start_container && push_down_start_container->IsTextNode() &&
-      push_down_start_offset ==
-          ToText(push_down_start_container)->length())
+  auto* push_down_start_container =
+      DynamicTo<Text>(push_down_start.ComputeContainerNode());
+  if (push_down_start_container &&
+      push_down_start_offset == push_down_start_container->length())
     push_down_start = NextVisuallyDistinctCandidate(push_down_start);
 
   // TODO(editing-dev): Use of UpdateStyleAndLayout
@@ -1579,7 +1579,7 @@
   else
     new_end = end;
 
-  Text* text = ToText(start.ComputeContainerNode());
+  auto* text = To<Text>(start.ComputeContainerNode());
   SplitTextNode(text, start.OffsetInContainerNode());
   UpdateStartEnd(EphemeralRange(Position::FirstPositionInNode(*text), new_end));
 }
@@ -1591,19 +1591,19 @@
   bool should_update_start =
       start.IsOffsetInAnchor() &&
       start.ComputeContainerNode() == end.ComputeContainerNode();
-  Text* text = ToText(end.AnchorNode());
+  auto* text = To<Text>(end.AnchorNode());
   SplitTextNode(text, end.OffsetInContainerNode());
 
-  Node* prev_node = text->previousSibling();
-  if (!prev_node || !prev_node->IsTextNode())
+  auto* prev_text_node = DynamicTo<Text>(text->previousSibling());
+  if (!prev_text_node)
     return;
 
   Position new_start =
       should_update_start
-          ? Position(ToText(prev_node), start.OffsetInContainerNode())
+          ? Position(prev_text_node, start.OffsetInContainerNode())
           : start;
   UpdateStartEnd(
-      EphemeralRange(new_start, Position::LastPositionInNode(*prev_node)));
+      EphemeralRange(new_start, Position::LastPositionInNode(*prev_text_node)));
 }
 
 void ApplyStyleCommand::SplitTextElementAtStart(const Position& start,
@@ -1618,7 +1618,7 @@
   else
     new_end = end;
 
-  SplitTextNodeContainingElement(ToText(start.ComputeContainerNode()),
+  SplitTextNodeContainingElement(To<Text>(start.ComputeContainerNode()),
                                  start.OffsetInContainerNode());
   UpdateStartEnd(EphemeralRange(
       Position::BeforeNode(*start.ComputeContainerNode()), new_end));
@@ -1630,19 +1630,21 @@
 
   bool should_update_start =
       start.ComputeContainerNode() == end.ComputeContainerNode();
-  SplitTextNodeContainingElement(ToText(end.ComputeContainerNode()),
+  SplitTextNodeContainingElement(To<Text>(end.ComputeContainerNode()),
                                  end.OffsetInContainerNode());
 
   Node* parent_element = end.ComputeContainerNode()->parentNode();
   if (!parent_element || !parent_element->previousSibling())
     return;
-  Node* first_text_node = parent_element->previousSibling()->lastChild();
-  if (!first_text_node || !first_text_node->IsTextNode())
+
+  auto* first_text_node =
+      DynamicTo<Text>(parent_element->previousSibling()->lastChild());
+  if (!first_text_node)
     return;
 
   Position new_start =
       should_update_start
-          ? Position(ToText(first_text_node), start.OffsetInContainerNode())
+          ? Position(first_text_node, start.OffsetInContainerNode())
           : start;
   UpdateStartEnd(
       EphemeralRange(new_start, Position::AfterNode(*first_text_node)));
@@ -2054,20 +2056,18 @@
   Position new_end = end;
 
   HeapVector<Member<Text>> text_nodes;
-  for (Node* curr = node->firstChild(); curr; curr = curr->nextSibling()) {
-    if (!curr->IsTextNode())
-      continue;
-
-    text_nodes.push_back(ToText(curr));
+  for (Node& child : NodeTraversal::ChildrenOf(*node)) {
+    if (auto* child_text = DynamicTo<Text>(child))
+      text_nodes.push_back(child_text);
   }
 
   for (const auto& text_node : text_nodes) {
     Text* child_text = text_node;
     Node* next = child_text->nextSibling();
-    if (!next || !next->IsTextNode())
+    auto* next_text = DynamicTo<Text>(next);
+    if (!next_text)
       continue;
 
-    Text* next_text = ToText(next);
     if (start.IsOffsetInAnchor() && next == start.ComputeContainerNode())
       new_start = Position(
           child_text, child_text->length() + start.OffsetInContainerNode());
diff --git a/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc b/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc
index 793dc9a..1e47940a 100644
--- a/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/break_blockquote_command.cc
@@ -184,8 +184,7 @@
   DCHECK(start_node);
 
   // Split at pos if in the middle of a text node.
-  if (start_node->IsTextNode()) {
-    Text* text_node = ToText(start_node);
+  if (auto* text_node = DynamicTo<Text>(start_node)) {
     int text_offset = pos.ComputeOffsetInContainerNode();
     if ((unsigned)text_offset >= text_node->length()) {
       start_node = NodeTraversal::Next(*start_node);
diff --git a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
index 4d39a2b..6dde6f4 100644
--- a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
@@ -329,6 +329,7 @@
   Node* ref_child = p.AnchorNode();
   int offset = p.OffsetInContainerNode();
 
+  auto* ref_child_text_node = DynamicTo<Text>(ref_child);
   if (CanHaveChildrenForEditing(ref_child)) {
     Node* child = ref_child->firstChild();
     for (int i = 0; child && i < offset; i++)
@@ -339,11 +340,11 @@
       AppendNode(insert_child, ToContainerNode(ref_child), editing_state);
   } else if (CaretMinOffset(ref_child) >= offset) {
     InsertNodeBefore(insert_child, ref_child, editing_state);
-  } else if (ref_child->IsTextNode() && CaretMaxOffset(ref_child) > offset) {
-    SplitTextNode(ToText(ref_child), offset);
+  } else if (ref_child_text_node && CaretMaxOffset(ref_child) > offset) {
+    SplitTextNode(ref_child_text_node, offset);
 
-    // Mutation events (bug 22634) from the text node insertion may have removed
-    // the refChild
+    // Mutation events (bug 22634) from the text node insertion may have
+    // removed the refChild
     if (!ref_child->isConnected())
       return;
     InsertNodeBefore(insert_child, ref_child, editing_state);
@@ -581,12 +582,11 @@
 Position CompositeEditCommand::ReplaceSelectedTextInNode(const String& text) {
   const Position& start = EndingSelection().Start();
   const Position& end = EndingSelection().End();
-  if (start.ComputeContainerNode() != end.ComputeContainerNode() ||
-      !start.ComputeContainerNode()->IsTextNode() ||
-      IsTabHTMLSpanElementTextNode(start.ComputeContainerNode()))
+  auto* text_node = DynamicTo<Text>(start.ComputeContainerNode());
+  if (!text_node || text_node != end.ComputeContainerNode() ||
+      IsTabHTMLSpanElementTextNode(text_node))
     return Position();
 
-  Text* text_node = ToText(start.ComputeContainerNode());
   ReplaceTextInNode(text_node, start.OffsetInContainerNode(),
                     end.OffsetInContainerNode() - start.OffsetInContainerNode(),
                     text);
@@ -624,7 +624,7 @@
   if (pos.OffsetInContainerNode() >= CaretMaxOffset(pos.ComputeContainerNode()))
     return Position::InParentAfterNode(*tab_span);
 
-  SplitTextNodeContainingElement(ToText(pos.ComputeContainerNode()),
+  SplitTextNodeContainingElement(To<Text>(pos.ComputeContainerNode()),
                                  pos.OffsetInContainerNode());
   return Position::InParentBeforeNode(*tab_span);
 }
@@ -683,12 +683,11 @@
   // needs to be audited.  See http://crbug.com/590369 for more details.
   GetDocument().UpdateStyleAndLayout();
 
-  Node* node = position.ComputeContainerNode();
-  if (!position.IsOffsetInAnchor() || !node || !node->IsTextNode() ||
-      !HasRichlyEditableStyle(*node))
+  auto* text_node = DynamicTo<Text>(position.ComputeContainerNode());
+  if (!position.IsOffsetInAnchor() || !text_node ||
+      !HasRichlyEditableStyle(*text_node))
     return false;
 
-  Text* text_node = ToText(node);
   if (text_node->length() == 0)
     return false;
 
@@ -709,14 +708,14 @@
   // If the rebalance is for the single offset, and neither text[offset] nor
   // text[offset - 1] are some form of whitespace, do nothing.
   int offset = position.ComputeOffsetInContainerNode();
-  String text = ToText(node)->data();
+  String text = To<Text>(node)->data();
   if (!IsWhitespace(text[offset])) {
     offset--;
     if (offset < 0 || !IsWhitespace(text[offset]))
       return;
   }
 
-  RebalanceWhitespaceOnTextSubstring(ToText(node),
+  RebalanceWhitespaceOnTextSubstring(To<Text>(node),
                                      position.OffsetInContainerNode(),
                                      position.OffsetInContainerNode());
 }
@@ -754,10 +753,10 @@
   // current text node. However, if the next sibling node is a text node
   // (not empty, see http://crbug.com/632300), we should use a plain space.
   // See http://crbug.com/310149
+  auto* next_text_node = DynamicTo<Text>(text_node->nextSibling());
   const bool next_sibling_is_text_node =
-      text_node->nextSibling() && text_node->nextSibling()->IsTextNode() &&
-      ToText(text_node->nextSibling())->data().length() &&
-      !IsWhitespace(ToText(text_node->nextSibling())->data()[0]);
+      next_text_node && next_text_node->data().length() &&
+      !IsWhitespace(next_text_node->data()[0]);
   const bool should_emit_nbs_pbefore_end =
       (IsEndOfParagraph(visible_downstream_pos) ||
        (unsigned)downstream == text.length()) &&
@@ -774,10 +773,10 @@
     Position& position) {
   if (!IsRichlyEditablePosition(position))
     return;
-  Node* node = position.AnchorNode();
-  if (!node || !node->IsTextNode())
+
+  auto* text_node = DynamicTo<Text>(position.AnchorNode());
+  if (!text_node)
     return;
-  Text* text_node = ToText(node);
 
   if (text_node->length() == 0)
     return;
@@ -807,10 +806,11 @@
   if (!IsCollapsibleWhitespace(CharacterAfter(visible_position)))
     return;
   Position pos = MostForwardCaretPosition(visible_position.DeepEquivalent());
-  if (!pos.ComputeContainerNode() || !pos.ComputeContainerNode()->IsTextNode())
+  auto* container_text_node = DynamicTo<Text>(pos.ComputeContainerNode());
+  if (!container_text_node)
     return;
-  ReplaceTextInNode(ToText(pos.ComputeContainerNode()),
-                    pos.OffsetInContainerNode(), 1, NonBreakingSpaceString());
+  ReplaceTextInNode(container_text_node, pos.OffsetInContainerNode(), 1,
+                    NonBreakingSpaceString());
 }
 
 void CompositeEditCommand::RebalanceWhitespace() {
@@ -919,8 +919,8 @@
 
   HeapVector<Member<Text>> nodes;
   for (Node& node : NodeTraversal::StartsAt(*start.AnchorNode())) {
-    if (node.IsTextNode())
-      nodes.push_back(ToText(&node));
+    if (auto* text_node = DynamicTo<Text>(&node))
+      nodes.push_back(text_node);
     if (&node == end.AnchorNode())
       break;
   }
@@ -1015,7 +1015,7 @@
     return;
   }
 
-  DeleteTextFromNode(ToText(p.AnchorNode()), p.OffsetInContainerNode(), 1);
+  DeleteTextFromNode(To<Text>(p.AnchorNode()), p.OffsetInContainerNode(), 1);
 }
 
 HTMLElement* CompositeEditCommand::InsertNewDefaultParagraphElementAt(
@@ -1295,7 +1295,7 @@
     } else if (LineBreakExistsAtPosition(position)) {
       // There is a preserved '\n' at caretAfterDelete.
       // We can safely assume this is a text node.
-      Text* text_node = ToText(node);
+      auto* text_node = To<Text>(node);
       if (text_node->length() == 1)
         RemoveNodeAndPruneAncestors(node, editing_state, destination_node);
       else
@@ -1829,9 +1829,8 @@
     RemoveNodeAndPruneAncestors(caret_pos.AnchorNode(), editing_state);
     if (editing_state->IsAborted())
       return false;
-  } else if (caret_pos.AnchorNode()->IsTextNode()) {
+  } else if (auto* text_node = DynamicTo<Text>(caret_pos.AnchorNode())) {
     DCHECK_EQ(caret_pos.ComputeOffsetInContainerNode(), 0);
-    Text* text_node = ToText(caret_pos.AnchorNode());
     ContainerNode* parent_node = text_node->parentNode();
     // The preserved newline must be the first thing in the node, since
     // otherwise the previous paragraph would be quoted, and we verified that it
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index dab8b54..186f8223 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -605,8 +605,8 @@
   }
 
   GetDocument().UpdateStyleAndLayout();
-  if (start_offset >= CaretMaxOffset(start_node) && start_node->IsTextNode()) {
-    Text* text = ToText(start_node);
+  auto* text = DynamicTo<Text>(start_node);
+  if (start_offset >= CaretMaxOffset(start_node) && text) {
     if (text->length() > (unsigned)CaretMaxOffset(start_node))
       DeleteTextFromNode(text, CaretMaxOffset(start_node),
                          text->length() - CaretMaxOffset(start_node));
@@ -623,9 +623,8 @@
 
   if (start_node == downstream_end_.AnchorNode()) {
     if (downstream_end_.ComputeEditingOffset() - start_offset > 0) {
-      if (start_node->IsTextNode()) {
+      if (auto* text = DynamicTo<Text>(start_node)) {
         // in a text node that needs to be trimmed
-        Text* text = ToText(start_node);
         DeleteTextFromNode(
             text, start_offset,
             downstream_end_.ComputeOffsetInContainerNode() - start_offset);
@@ -654,20 +653,19 @@
             downstream_end_.AnchorNode());
     // The selection to delete spans more than one node.
     Node* node(start_node);
-
+    auto* start_text_node = DynamicTo<Text>(start_node);
     if (start_offset > 0) {
-      if (start_node->IsTextNode()) {
+      if (start_text_node) {
         // in a text node that needs to be trimmed
-        Text* text = ToText(node);
-        DeleteTextFromNode(text, start_offset, text->length() - start_offset);
+        DeleteTextFromNode(start_text_node, start_offset,
+                           start_text_node->length() - start_offset);
         node = NodeTraversal::Next(*node);
       } else {
         node = NodeTraversal::ChildAt(*start_node, start_offset);
       }
-    } else if (start_node == upstream_end_.AnchorNode() &&
-               start_node->IsTextNode()) {
-      Text* text = ToText(upstream_end_.AnchorNode());
-      DeleteTextFromNode(text, 0, upstream_end_.ComputeOffsetInContainerNode());
+    } else if (start_node == upstream_end_.AnchorNode() && start_text_node) {
+      DeleteTextFromNode(start_text_node, 0,
+                         upstream_end_.ComputeOffsetInContainerNode());
     }
 
     // handle deleting all nodes that are completely selected
@@ -716,9 +714,8 @@
         // The node itself is fully selected, not just its contents.  Delete it.
         RemoveNode(downstream_end_.AnchorNode(), editing_state);
       } else {
-        if (downstream_end_.AnchorNode()->IsTextNode()) {
+        if (auto* text = DynamicTo<Text>(downstream_end_.AnchorNode())) {
           // in a text node that needs to be trimmed
-          Text* text = ToText(downstream_end_.AnchorNode());
           if (downstream_end_.ComputeEditingOffset() > 0) {
             DeleteTextFromNode(text, 0, downstream_end_.ComputeEditingOffset());
           }
@@ -756,26 +753,27 @@
 void DeleteSelectionCommand::FixupWhitespace() {
   GetDocument().UpdateStyleAndLayout();
   if (leading_whitespace_.IsNotNull() &&
-      !IsRenderedCharacter(leading_whitespace_) &&
-      leading_whitespace_.AnchorNode()->IsTextNode()) {
-    Text* text_node = ToText(leading_whitespace_.AnchorNode());
-    DCHECK(!text_node->GetLayoutObject() ||
-           text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
-        << text_node;
-    ReplaceTextInNode(text_node,
-                      leading_whitespace_.ComputeOffsetInContainerNode(), 1,
-                      NonBreakingSpaceString());
+      !IsRenderedCharacter(leading_whitespace_)) {
+    if (auto* text_node = DynamicTo<Text>(leading_whitespace_.AnchorNode())) {
+      DCHECK(!text_node->GetLayoutObject() ||
+             text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
+          << text_node;
+      ReplaceTextInNode(text_node,
+                        leading_whitespace_.ComputeOffsetInContainerNode(), 1,
+                        NonBreakingSpaceString());
+    }
   }
+
   if (trailing_whitespace_.IsNotNull() &&
-      !IsRenderedCharacter(trailing_whitespace_) &&
-      trailing_whitespace_.AnchorNode()->IsTextNode()) {
-    Text* text_node = ToText(trailing_whitespace_.AnchorNode());
-    DCHECK(!text_node->GetLayoutObject() ||
-           text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
-        << text_node;
-    ReplaceTextInNode(text_node,
-                      trailing_whitespace_.ComputeOffsetInContainerNode(), 1,
-                      NonBreakingSpaceString());
+      !IsRenderedCharacter(trailing_whitespace_)) {
+    if (auto* text_node = DynamicTo<Text>(trailing_whitespace_.AnchorNode())) {
+      DCHECK(!text_node->GetLayoutObject() ||
+             text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
+          << text_node;
+      ReplaceTextInNode(text_node,
+                        trailing_whitespace_.ComputeOffsetInContainerNode(), 1,
+                        NonBreakingSpaceString());
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
index 32424ba..b5d99f71 100644
--- a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
+++ b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/renderer/core/html/html_html_element.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
@@ -255,11 +256,10 @@
   if (!position.AnchorNode()->GetLayoutObject())
     return false;
 
-  if (!position.AnchorNode()->IsTextNode() ||
-      !position.AnchorNode()->GetLayoutObject()->Style()->PreserveNewline())
+  const auto* text_node = DynamicTo<Text>(position.AnchorNode());
+  if (!text_node || !text_node->GetLayoutObject()->Style()->PreserveNewline())
     return false;
 
-  const Text* text_node = ToText(position.AnchorNode());
   unsigned offset = position.OffsetInContainerNode();
   return offset < text_node->length() && text_node->data()[offset] == '\n';
 }
@@ -317,7 +317,8 @@
   if (prev == position)
     return Position();
   const Node* const anchor_node = prev.AnchorNode();
-  if (!anchor_node || !anchor_node->IsTextNode())
+  auto* anchor_text_node = DynamicTo<Text>(anchor_node);
+  if (!anchor_text_node)
     return Position();
   if (EnclosingBlockFlowElement(*anchor_node) !=
       EnclosingBlockFlowElement(*position.AnchorNode()))
@@ -326,7 +327,7 @@
       anchor_node->GetLayoutObject() &&
       !anchor_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
     return Position();
-  const String& string = ToText(anchor_node)->data();
+  const String& string = anchor_text_node->data();
   const UChar previous_character = string[prev.ComputeOffsetInContainerNode()];
   const bool is_space = option == kConsiderNonCollapsibleWhitespace
                             ? (IsSpaceOrNewline(previous_character) ||
diff --git a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
index a1ef62ef..edc26d5 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
@@ -163,9 +163,8 @@
         SelectionInDOMTree::Builder()
             .Collapse(Position::InParentAfterNode(*node_to_insert))
             .Build()));
-  } else if (pos.AnchorNode()->IsTextNode()) {
+  } else if (auto* text_node = DynamicTo<Text>(pos.AnchorNode())) {
     // Split a text node
-    Text* text_node = ToText(pos.AnchorNode());
     SplitTextNode(text_node, pos.ComputeOffsetInContainerNode());
     InsertNodeBefore(node_to_insert, text_node, editing_state);
     if (editing_state->IsAborted())
diff --git a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
index 432da89f..5af3a30d 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
@@ -489,31 +489,33 @@
   // FIXME: leadingCollapsibleWhitespacePosition is returning the position
   // before preserved newlines for positions after the preserved newline,
   // causing the newline to be turned into a nbsp.
-  if (leading_whitespace.IsNotNull() &&
-      leading_whitespace.AnchorNode()->IsTextNode()) {
-    Text* text_node = ToText(leading_whitespace.AnchorNode());
-    DCHECK(!text_node->GetLayoutObject() ||
-           text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
-        << text_node;
-    ReplaceTextInNode(text_node,
-                      leading_whitespace.ComputeOffsetInContainerNode(), 1,
-                      NonBreakingSpaceString());
-    GetDocument().UpdateStyleAndLayout();
+  if (leading_whitespace.IsNotNull()) {
+    if (auto* text_node = DynamicTo<Text>(leading_whitespace.AnchorNode())) {
+      DCHECK(!text_node->GetLayoutObject() ||
+             text_node->GetLayoutObject()->Style()->CollapseWhiteSpace())
+          << text_node;
+      ReplaceTextInNode(text_node,
+                        leading_whitespace.ComputeOffsetInContainerNode(), 1,
+                        NonBreakingSpaceString());
+      GetDocument().UpdateStyleAndLayout();
+    }
   }
 
   // Split at pos if in the middle of a text node.
   Position position_after_split;
-  if (insertion_position.IsOffsetInAnchor() &&
-      insertion_position.ComputeContainerNode()->IsTextNode()) {
-    Text* text_node = ToText(insertion_position.ComputeContainerNode());
-    int text_offset = insertion_position.OffsetInContainerNode();
-    bool at_end = static_cast<unsigned>(text_offset) >= text_node->length();
-    if (text_offset > 0 && !at_end) {
-      SplitTextNode(text_node, text_offset);
-      GetDocument().UpdateStyleAndLayout();
+  if (insertion_position.IsOffsetInAnchor()) {
+    if (auto* text_node =
+            DynamicTo<Text>(insertion_position.ComputeContainerNode())) {
+      int text_offset = insertion_position.OffsetInContainerNode();
+      bool at_end = static_cast<unsigned>(text_offset) >= text_node->length();
+      if (text_offset > 0 && !at_end) {
+        SplitTextNode(text_node, text_offset);
+        GetDocument().UpdateStyleAndLayout();
 
-      position_after_split = Position::FirstPositionInNode(*text_node);
-      insertion_position = Position(text_node->previousSibling(), text_offset);
+        position_after_split = Position::FirstPositionInNode(*text_node);
+        insertion_position =
+            Position(text_node->previousSibling(), text_offset);
+      }
     }
   }
 
diff --git a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
index a7c2b33..ff0ac87 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
@@ -119,11 +119,8 @@
 
 bool InsertTextCommand::PerformOverwrite(const String& text) {
   Position start = EndingVisibleSelection().Start();
-  if (start.IsNull() || !start.IsOffsetInAnchor() ||
-      !start.ComputeContainerNode()->IsTextNode())
-    return false;
-  Text* text_node = ToText(start.ComputeContainerNode());
-  if (!text_node)
+  auto* text_node = DynamicTo<Text>(start.ComputeContainerNode());
+  if (start.IsNull() || !start.IsOffsetInAnchor() || !text_node)
     return false;
 
   unsigned count = std::min(
@@ -312,11 +309,11 @@
     return pos;
 
   Node* node = insert_pos.ComputeContainerNode();
-  unsigned offset = node->IsTextNode() ? insert_pos.OffsetInContainerNode() : 0;
+  auto* text_node = DynamicTo<Text>(node);
+  unsigned offset = text_node ? insert_pos.OffsetInContainerNode() : 0;
 
   // keep tabs coalesced in tab span
   if (IsTabHTMLSpanElementTextNode(node)) {
-    Text* text_node = ToText(node);
     InsertTextIntoNode(text_node, offset, "\t");
     return Position(text_node, offset + 1);
   }
@@ -325,7 +322,7 @@
   HTMLSpanElement* span_element = CreateTabSpanElement(GetDocument());
 
   // place it
-  if (!node->IsTextNode()) {
+  if (!text_node) {
     InsertNodeAt(span_element, insert_pos, editing_state);
   } else {
     Text* text_node = ToText(node);
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
index d3116ab..6c716da1 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -777,9 +777,8 @@
     InsertedNodes& inserted_nodes) {
   GetDocument().UpdateStyleAndLayout();
 
-  Node* last_leaf_inserted = inserted_nodes.LastLeafInserted();
-  if (last_leaf_inserted && last_leaf_inserted->IsTextNode() &&
-      !NodeHasVisibleLayoutText(ToText(*last_leaf_inserted)) &&
+  auto* last_leaf_inserted = DynamicTo<Text>(inserted_nodes.LastLeafInserted());
+  if (last_leaf_inserted && !NodeHasVisibleLayoutText(*last_leaf_inserted) &&
       !EnclosingElementWithTag(FirstPositionInOrBeforeNode(*last_leaf_inserted),
                                kSelectTag) &&
       !EnclosingElementWithTag(FirstPositionInOrBeforeNode(*last_leaf_inserted),
@@ -792,9 +791,9 @@
   // We don't have to make sure that firstNodeInserted isn't inside a select or
   // script element, because it is a top level node in the fragment and the user
   // can't insert into those elements.
-  Node* first_node_inserted = inserted_nodes.FirstNodeInserted();
-  if (first_node_inserted && first_node_inserted->IsTextNode() &&
-      !NodeHasVisibleLayoutText(ToText(*first_node_inserted))) {
+  auto* first_node_inserted =
+      DynamicTo<Text>(inserted_nodes.FirstNodeInserted());
+  if (first_node_inserted && !NodeHasVisibleLayoutText(*first_node_inserted)) {
     inserted_nodes.WillRemoveNode(*first_node_inserted);
     // Removing a Text node won't dispatch synchronous events.
     RemoveNode(first_node_inserted, ASSERT_NO_EDITING_ABORT);
@@ -1299,11 +1298,10 @@
   // our style spans and for positions inside list items
   // since insertAsListItems already does the right thing.
   if (!match_style_ && !EnclosingList(insertion_pos.ComputeContainerNode())) {
-    if (insertion_pos.ComputeContainerNode()->IsTextNode() &&
-        insertion_pos.OffsetInContainerNode() &&
+    auto* text_node = DynamicTo<Text>(insertion_pos.ComputeContainerNode());
+    if (text_node && insertion_pos.OffsetInContainerNode() &&
         !insertion_pos.AtLastEditingPositionForNode()) {
-      SplitTextNode(ToText(insertion_pos.ComputeContainerNode()),
-                    insertion_pos.OffsetInContainerNode());
+      SplitTextNode(text_node, insertion_pos.OffsetInContainerNode());
       insertion_pos =
           Position::FirstPositionInNode(*insertion_pos.ComputeContainerNode());
     }
@@ -1708,8 +1706,8 @@
   Position end_upstream =
       MostBackwardCaretPosition(end_of_inserted_content.DeepEquivalent());
   Node* end_node = end_upstream.ComputeNodeBeforePosition();
-  int end_offset =
-      end_node && end_node->IsTextNode() ? ToText(end_node)->length() : 0;
+  auto* end_text_node = DynamicTo<Text>(end_node);
+  int end_offset = end_text_node ? end_text_node->length() : 0;
   if (end_upstream.IsOffsetInAnchor()) {
     end_node = end_upstream.ComputeContainerNode();
     end_offset = end_upstream.OffsetInContainerNode();
@@ -1723,8 +1721,8 @@
     bool collapse_white_space =
         !end_node->GetLayoutObject() ||
         end_node->GetLayoutObject()->Style()->CollapseWhiteSpace();
-    if (end_node->IsTextNode()) {
-      InsertTextIntoNode(ToText(end_node), end_offset,
+    if (auto* end_text_node = DynamicTo<Text>(end_node)) {
+      InsertTextIntoNode(end_text_node, end_offset,
                          collapse_white_space ? NonBreakingSpaceString() : " ");
       if (end_of_inserted_content_.ComputeContainerNode() == end_node)
         end_of_inserted_content_ = Position(
@@ -1763,8 +1761,8 @@
     bool collapse_white_space =
         !start_node->GetLayoutObject() ||
         start_node->GetLayoutObject()->Style()->CollapseWhiteSpace();
-    if (start_node->IsTextNode()) {
-      InsertTextIntoNode(ToText(start_node), start_offset,
+    if (auto* start_text_node = DynamicTo<Text>(start_node)) {
+      InsertTextIntoNode(start_text_node, start_offset,
                          collapse_white_space ? NonBreakingSpaceString() : " ");
       if (end_of_inserted_content_.ComputeContainerNode() == start_node &&
           end_of_inserted_content_.OffsetInContainerNode())
@@ -1852,19 +1850,17 @@
   bool position_only_to_be_updated_is_offset_in_anchor =
       position_only_to_be_updated.IsOffsetInAnchor();
   Text* text = nullptr;
-  if (position_is_offset_in_anchor && position.ComputeContainerNode() &&
-      position.ComputeContainerNode()->IsTextNode()) {
-    text = ToText(position.ComputeContainerNode());
-  } else {
-    Node* before = position.ComputeNodeBeforePosition();
-    if (before && before->IsTextNode()) {
-      text = ToText(before);
-    } else {
-      Node* after = position.ComputeNodeAfterPosition();
-      if (after && after->IsTextNode())
-        text = ToText(after);
-    }
+  auto* container_text_node = DynamicTo<Text>(position.ComputeContainerNode());
+  if (position_is_offset_in_anchor && container_text_node) {
+    text = container_text_node;
+  } else if (auto* before =
+                 DynamicTo<Text>(position.ComputeNodeBeforePosition())) {
+    text = before;
+  } else if (auto* after =
+                 DynamicTo<Text>(position.ComputeNodeAfterPosition())) {
+    text = after;
   }
+
   if (!text)
     return;
 
@@ -1878,8 +1874,7 @@
        U16_IS_LEAD(text->data()[text->data().length() - 1]));
   if (!has_incomplete_surrogate && text->data().length() > kMergeSizeLimit)
     return;
-  if (text->previousSibling() && text->previousSibling()->IsTextNode()) {
-    Text* previous = ToText(text->previousSibling());
+  if (auto* previous = DynamicTo<Text>(text->previousSibling())) {
     if (has_incomplete_surrogate ||
         previous->data().length() <= kMergeSizeLimit) {
       InsertTextIntoNode(text, 0, previous->data());
@@ -1966,8 +1961,9 @@
   // list items and insert these nodes between them.
   if (is_middle) {
     int text_node_offset = insert_pos.OffsetInContainerNode();
-    if (insert_pos.AnchorNode()->IsTextNode() && text_node_offset > 0)
-      SplitTextNode(ToText(insert_pos.AnchorNode()), text_node_offset);
+    auto* text_node = DynamicTo<Text>(insert_pos.AnchorNode());
+    if (text_node && text_node_offset > 0)
+      SplitTextNode(text_node, text_node_offset);
     SplitTreeToNode(insert_pos.AnchorNode(), last_node, true);
   }
 
@@ -2036,7 +2032,7 @@
 
   Node* node_after_insertion_pos =
       MostForwardCaretPosition(EndingSelection().End()).AnchorNode();
-  Text* text_node = ToText(fragment.FirstChild());
+  auto* text_node = To<Text>(fragment.FirstChild());
   // Our fragment creation code handles tabs, spaces, and newlines, so we don't
   // have to worry about those here.
 
diff --git a/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc b/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc
index 6852ce3..fc1cf77 100644
--- a/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/set_character_data_command_test.cc
@@ -16,51 +16,53 @@
   SetBodyContent("<div contenteditable>This is a good test case</div>");
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "lame");
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4,
+      "lame");
 
   command->DoReapply();
   EXPECT_EQ(
       "This is a lame test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "This is a good test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, replaceTextWithLongerText) {
   SetBodyContent("<div contenteditable>This is a good test case</div>");
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "lousy");
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4,
+      "lousy");
 
   command->DoReapply();
   EXPECT_EQ(
       "This is a lousy test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "This is a good test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, replaceTextWithShorterText) {
   SetBodyContent("<div contenteditable>This is a good test case</div>");
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 10, 4, "meh");
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 10, 4, "meh");
 
   command->DoReapply();
   EXPECT_EQ(
       "This is a meh test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "This is a good test case",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, insertTextIntoEmptyNode) {
@@ -70,52 +72,53 @@
       GetDocument().CreateEditingTextNode(""));
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 0, 0, "hello");
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 0, 0,
+      "hello");
 
   command->DoReapply();
   EXPECT_EQ(
       "hello",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, insertTextAtEndOfNonEmptyNode) {
   SetBodyContent("<div contenteditable>Hello</div>");
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 5, 0,
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 5, 0,
       ", world!");
 
   command->DoReapply();
   EXPECT_EQ(
       "Hello, world!",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "Hello",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, replaceEntireNode) {
   SetBodyContent("<div contenteditable>Hello</div>");
 
   SimpleEditCommand* command = MakeGarbageCollected<SetCharacterDataCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 0, 5, "Bye");
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 0, 5, "Bye");
 
   command->DoReapply();
   EXPECT_EQ(
       "Bye",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 
   command->DoUnapply();
   EXPECT_EQ(
       "Hello",
-      ToText(GetDocument().body()->firstChild()->firstChild())->wholeText());
+      To<Text>(GetDocument().body()->firstChild()->firstChild())->wholeText());
 }
 
 TEST_F(SetCharacterDataCommandTest, CombinedText) {
@@ -123,7 +126,7 @@
       "<div contenteditable style='writing-mode:vertical-lr; "
       "-webkit-text-combine:horizontal' />");
 
-  Text* text_node = ToText(GetDocument().body()->firstChild()->appendChild(
+  auto* text_node = To<Text>(GetDocument().body()->firstChild()->appendChild(
       GetDocument().CreateEditingTextNode("")));
   UpdateAllLifecyclePhasesForTest();
 
diff --git a/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc b/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc
index a8786bc..83fe25745 100644
--- a/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/split_text_node_command_test.cc
@@ -32,13 +32,13 @@
       range, TextMatchMarker::MatchStatus::kInactive);
 
   SimpleEditCommand* command = MakeGarbageCollected<SplitTextNodeCommand>(
-      ToText(GetDocument().body()->firstChild()->firstChild()), 8);
+      To<Text>(GetDocument().body()->firstChild()->firstChild()), 8);
 
   EditingState editingState;
   command->DoApply(&editingState);
 
-  const Text& text1 = ToText(*div->firstChild());
-  const Text& text2 = ToText(*text1.nextSibling());
+  const Text& text1 = To<Text>(*div->firstChild());
+  const Text& text2 = To<Text>(*text1.nextSibling());
 
   // The first marker should end up in text1, the second marker should be
   // truncated and end up text1, the third marker should end up in text2
@@ -60,7 +60,7 @@
   // Test undo
   command->DoUnapply();
 
-  const Text& text = ToText(*div->firstChild());
+  const Text& text = To<Text>(*div->firstChild());
 
   EXPECT_EQ(3u, GetDocument().Markers().MarkersFor(text).size());
 
@@ -78,8 +78,8 @@
   // Test redo
   command->DoReapply();
 
-  const Text& text3 = ToText(*div->firstChild());
-  const Text& text4 = ToText(*text3.nextSibling());
+  const Text& text3 = To<Text>(*div->firstChild());
+  const Text& text4 = To<Text>(*text3.nextSibling());
 
   EXPECT_EQ(2u, GetDocument().Markers().MarkersFor(text3).size());
 
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.cc b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
index 1dd9498..ebfeb55 100644
--- a/third_party/blink/renderer/core/editing/editing_style_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
@@ -153,10 +153,10 @@
   // want Position("world", 0) instead.
   // We only do this for range because caret at Position("hello", 5) in
   // <b>hello</b>world should give you font-weight: bold.
-  Node* position_node = position.ComputeContainerNode();
-  if (selection.IsRange() && position_node && position_node->IsTextNode() &&
+  auto* position_node = DynamicTo<Text>(position.ComputeContainerNode());
+  if (selection.IsRange() && position_node &&
       position.ComputeOffsetInContainerNode() ==
-          static_cast<int>(ToText(position_node)->length()))
+          static_cast<int>(position_node->length()))
     position = NextVisuallyDistinctCandidate(position);
 
   Element* element = AssociatedElementOf(position);
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index f64beba..ce0457c 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -803,9 +803,10 @@
 int PreviousGraphemeBoundaryOf(const Node& node, int current) {
   // TODO(yosin): Need to support grapheme crossing |Node| boundary.
   DCHECK_GE(current, 0);
-  if (current <= 1 || !node.IsTextNode())
+  auto* text_node = DynamicTo<Text>(node);
+  if (current <= 1 || !text_node)
     return current - 1;
-  const String& text = ToText(node).data();
+  const String& text = text_node->data();
   // TODO(yosin): Replace with DCHECK for out-of-range request.
   if (static_cast<unsigned>(current) > text.length())
     return current - 1;
@@ -817,19 +818,21 @@
   DCHECK_GE(current, 0);
   if (current <= 1)
     return 0;
-  if (!node.IsTextNode())
+  auto* text_node = DynamicTo<Text>(node);
+  if (!text_node)
     return current - 1;
 
-  const String& text = ToText(node).data();
+  const String& text = text_node->data();
   DCHECK_LT(static_cast<unsigned>(current - 1), text.length());
   return FindNextBoundaryOffset<BackspaceStateMachine>(text, current);
 }
 
 int NextGraphemeBoundaryOf(const Node& node, int current) {
   // TODO(yosin): Need to support grapheme crossing |Node| boundary.
-  if (!node.IsTextNode())
+  auto* text_node = DynamicTo<Text>(node);
+  if (!text_node)
     return current + 1;
-  const String& text = ToText(node).data();
+  const String& text = text_node->data();
   const int length = text.length();
   DCHECK_LE(current, length);
   if (current >= length - 1)
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index 21d575b..beb4b73 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -154,11 +154,11 @@
   // character is not a space, but typing another space will do.
   Position prev =
       PreviousPositionOf(position, PositionMoveType::kGraphemeCluster);
-  const Node* prev_node = prev.ComputeContainerNode();
-  if (!prev_node || !prev_node->IsTextNode())
+  const auto* prev_node = DynamicTo<Text>(prev.ComputeContainerNode());
+  if (!prev_node)
     return false;
   int prev_offset = prev.ComputeOffsetInContainerNode();
-  UChar prev_char = ToText(prev_node)->data()[prev_offset];
+  UChar prev_char = prev_node->data()[prev_offset];
   return prev_char == kSpaceCharacter;
 }
 
diff --git a/third_party/blink/renderer/core/editing/editor_test.cc b/third_party/blink/renderer/core/editing/editor_test.cc
index 941d900..50bc89f 100644
--- a/third_party/blink/renderer/core/editing/editor_test.cc
+++ b/third_party/blink/renderer/core/editing/editor_test.cc
@@ -113,7 +113,7 @@
   const SelectionInDOMTree selection = SetSelectionTextToBody(
       "<div contenteditable><div></div><b>^abc|</b></div>");
   Selection().SetSelection(selection, SetSelectionOptions());
-  Text& abc = ToText(*selection.Base().ComputeContainerNode());
+  auto& abc = To<Text>(*selection.Base().ComputeContainerNode());
   // Push Text node "abc" into undo stack
   GetDocument().execCommand("italic", false, "", ASSERT_NO_EXCEPTION);
   // Change Text node "abc" in undo stack
diff --git a/third_party/blink/renderer/core/editing/element_inner_text.cc b/third_party/blink/renderer/core/editing/element_inner_text.cc
index 2d176eeb..1979656 100644
--- a/third_party/blink/renderer/core/editing/element_inner_text.cc
+++ b/third_party/blink/renderer/core/editing/element_inner_text.cc
@@ -291,8 +291,9 @@
   }
 
   // 4. If node is a Text node, then for each CSS text box produced by node.
-  if (node.IsTextNode())
-    return ProcessTextNode(ToText(node));
+  auto* text_node = DynamicTo<Text>(node);
+  if (text_node)
+    return ProcessTextNode(*text_node);
 
   // 5. If node is a br element, then append a string containing a single U+000A
   // LINE FEED (LF) character to items.
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
index 68390d28..77df41b7 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -399,17 +399,17 @@
       continue;
     }
     // This node is in its own sub-block separate from our starting position.
-    if (first_traversed_node != node && !node->IsTextNode() &&
+    const auto* text_node = DynamicTo<Text>(node);
+    if (first_traversed_node != node && !text_node &&
         IsBlock(style->Display())) {
       break;
     }
 
-    if (style->Visibility() == EVisibility::kVisible && node->IsTextNode() &&
+    if (style->Visibility() == EVisibility::kVisible && text_node &&
         node->GetLayoutObject()) {
-      const Text& text_node = ToText(*node);
       LayoutBlockFlow& block_flow =
           *NGOffsetMapping::GetInlineFormattingContextOf(
-              *text_node.GetLayoutObject());
+              *text_node->GetLayoutObject());
       if (last_block_flow && last_block_flow != block_flow) {
         // We enter another block flow.
         break;
@@ -417,7 +417,7 @@
       if (!last_block_flow) {
         last_block_flow = &block_flow;
       }
-      AddTextToBuffer(text_node, block_flow, range);
+      AddTextToBuffer(*text_node, block_flow, range);
     }
     if (node == end_node) {
       node = FlatTreeTraversal::Next(*node);
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
index 5177fdf9..90d4f1da 100644
--- a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
@@ -1477,16 +1477,16 @@
       PlainTextRange(2).CreateRange(*div).StartPosition();
   const Position& second_line_position =
       PlainTextRange(8).CreateRange(*div).StartPosition();
-  ASSERT_EQ(0u,
-            GetDocument()
-                .Markers()
-                .MarkersFor(ToText(*first_line_position.ComputeContainerNode()))
-                .size());
   ASSERT_EQ(
-      1u, GetDocument()
+      0u, GetDocument()
               .Markers()
-              .MarkersFor(ToText(*second_line_position.ComputeContainerNode()))
+              .MarkersFor(To<Text>(*first_line_position.ComputeContainerNode()))
               .size());
+  ASSERT_EQ(1u, GetDocument()
+                    .Markers()
+                    .MarkersFor(
+                        To<Text>(*second_line_position.ComputeContainerNode()))
+                    .size());
 
   // Verify marker has correct start/end offsets (measured from the beginning
   // of the node, which is the beginning of the line)
@@ -1628,7 +1628,7 @@
   // either space around it
   EXPECT_EQ(
       1u,
-      GetDocument().Markers().MarkersFor(ToText(*div->firstChild())).size());
+      GetDocument().Markers().MarkersFor(To<Text>(*div->firstChild())).size());
   EXPECT_STREQ("text",
                GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
                    .Utf8()
@@ -2450,8 +2450,8 @@
   Controller().SetComposition("test", ime_text_spans, 0, 4);
 
   Node* b = div->firstChild();
-  Text* text1 = ToText(b->firstChild());
-  Text* text2 = ToText(b->nextSibling());
+  auto* text1 = To<Text>(b->firstChild());
+  auto* text2 = To<Text>(b->nextSibling());
 
   const DocumentMarkerVector& text1_markers =
       GetDocument().Markers().MarkersFor(
@@ -2482,7 +2482,7 @@
   Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1);
 
   EXPECT_EQ(1u, div->CountChildren());
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
   EXPECT_EQ("t", text->data());
 }
 
@@ -2498,7 +2498,7 @@
   Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1);
 
   EXPECT_EQ(1u, div->CountChildren());
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
   EXPECT_EQ("t", text->data());
 }
 
@@ -2515,7 +2515,7 @@
   Controller().SetComposition("t", Vector<ImeTextSpan>(), 0, 1);
 
   EXPECT_EQ(1u, div->CountChildren());
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
   EXPECT_EQ("t", text->data());
 }
 
@@ -2572,7 +2572,7 @@
                               Vector<ImeTextSpan>(), 1, 1);
 
   EXPECT_EQ(1u, div->CountChildren());
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
   EXPECT_STREQ("\xE0\xAE\x9A\xE0\xAF\x8D\xE0\xAE\x9A",
                text->data().Utf8().data());
 
diff --git a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
index f589536..e3fdfb2e 100644
--- a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
@@ -241,7 +241,7 @@
   const int text_length = position_end_offset - position_start_offset;
   const int text_offset = position_start_offset - offset_in_node;
   CHECK_LE(static_cast<unsigned>(text_offset + text_length), text.length());
-  text_state_.EmitText(ToText(*node_), position_start_offset,
+  text_state_.EmitText(To<Text>(*node_), position_start_offset,
                        position_end_offset, text, text_offset,
                        text_offset + text_length);
   return !should_handle_first_letter_;
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
index e04a90b..2e84763a 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -477,7 +477,7 @@
 
   DCHECK_NE(last_text_node_, node_)
       << "We should never call HandleTextNode on the same node twice";
-  const Text* text = ToText(node_);
+  const auto* text = To<Text>(node_.Get());
   last_text_node_ = text;
 
   // TODO(editing-dev): Introduce a |DOMOffsetRange| class so that we can pass
@@ -919,9 +919,9 @@
     return PositionTemplate<Strategy>(
         node, text_state_.PositionStartOffset() + char16_offset);
   }
-  if (node.IsTextNode()) {
+  if (auto* text_node = DynamicTo<Text>(node)) {
     if (text_state_.IsAfterPositionNode())
-      return PositionTemplate<Strategy>(node, ToText(node).length());
+      return PositionTemplate<Strategy>(node, text_node->length());
     return PositionTemplate<Strategy>(node, 0);
   }
   if (text_state_.IsAfterPositionNode())
@@ -950,10 +950,10 @@
     return PositionTemplate<Strategy>(
         node, text_state_.PositionStartOffset() + char16_offset + 1);
   }
-  if (node.IsTextNode()) {
+  if (auto* text_node = DynamicTo<Text>(node)) {
     if (text_state_.IsBeforePositionNode())
       return PositionTemplate<Strategy>(node, 0);
-    return PositionTemplate<Strategy>(node, ToText(node).length());
+    return PositionTemplate<Strategy>(node, text_node->length());
   }
   if (text_state_.IsBeforePositionNode())
     return PositionTemplate<Strategy>::BeforeNode(node);
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc
index 5cbe5c5..e45524c 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -457,12 +457,13 @@
 static base::Optional<unsigned> ComputeEndOffset(
     const Node& node,
     const PositionInFlatTree& selection_end) {
-  if (!node.IsTextNode())
+  auto* text_node = DynamicTo<Text>(node);
+  if (!text_node)
     return base::nullopt;
 
   if (&node == selection_end.AnchorNode())
     return selection_end.OffsetInContainerNode();
-  return ToText(node).length();
+  return text_node->length();
 }
 
 #if DCHECK_IS_ON()
@@ -597,7 +598,7 @@
   if (const LayoutTextFragment* fragment = ToLayoutTextFragmentOrNull(text))
     return fragment->AssociatedTextNode();
   if (Node* node = text.GetNode())
-    return ToTextOrNull(node);
+    return DynamicTo<Text>(node);
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc
index 83c9b67..28b1d5b 100644
--- a/third_party/blink/renderer/core/editing/layout_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -81,7 +81,7 @@
                                  std::ostream& ostream,
                                  const Node& node,
                                  wtf_size_t depth) {
-    if (const Text* text = ToTextOrNull(node))
+    if (const Text* text = DynamicTo<Text>(node))
       PrintText(ostream, *text);
     else if (const Element* element = ToElementOrNull(node))
       ostream << element->tagName().Utf8().data();
@@ -981,8 +981,8 @@
 
   const Text* GetFirstTextNode() {
     for (const Node& runner : NodeTraversal::StartsAt(*GetDocument().body())) {
-      if (runner.IsTextNode())
-        return &ToText(runner);
+      if (auto* text_node = DynamicTo<Text>(runner))
+        return text_node;
     }
     NOTREACHED();
     return nullptr;
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
index 09c7724c..7df3613 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
@@ -227,11 +227,12 @@
       return;
     DCHECK(!markers_.IsEmpty());
     const Node& node = marked_text.CurrentContainer();
-    if (!node.IsTextNode())
+    auto* text_node = DynamicTo<Text>(node);
+    if (!text_node)
       continue;
     int start_offset = marked_text.StartOffsetInCurrentContainer();
     int end_offset = marked_text.EndOffsetInCurrentContainer();
-    RemoveMarkersInternal(ToText(node), start_offset, end_offset - start_offset,
+    RemoveMarkersInternal(*text_node, start_offset, end_offset - start_offset,
                           marker_types);
   }
 }
@@ -266,13 +267,13 @@
 
     // Ignore text emitted by TextIterator for non-text nodes (e.g. implicit
     // newlines)
-    const Node& node = marked_text.CurrentContainer();
-    if (!node.IsTextNode())
+    const auto* text_node = DynamicTo<Text>(marked_text.CurrentContainer());
+    if (!text_node)
       continue;
 
     DocumentMarker* const new_marker = create_marker_from_offsets(
         start_offset_in_current_container, end_offset_in_current_container);
-    AddMarkerToNode(ToText(node), new_marker);
+    AddMarkerToNode(*text_node, new_marker);
   }
 }
 
@@ -423,15 +424,16 @@
   const unsigned end_offset = end.ComputeOffsetInContainerNode();
 
   for (const Node& node : EphemeralRangeInFlatTree(start, end).Nodes()) {
-    if (!node.IsTextNode())
+    auto* text_node = DynamicTo<Text>(node);
+    if (!text_node)
       continue;
 
     const unsigned start_range_offset = node == start_node ? start_offset : 0;
     const unsigned end_range_offset =
-        node == end_node ? end_offset : ToText(node).length();
+        node == end_node ? end_offset : text_node->length();
 
     DocumentMarker* const found_marker = FirstMarkerIntersectingOffsetRange(
-        ToText(node), start_range_offset, end_range_offset, types);
+        *text_node, start_range_offset, end_range_offset, types);
     if (found_marker)
       return found_marker;
   }
@@ -459,7 +461,8 @@
   if (start_container != end_container)
     return nullptr;
 
-  if (!start_container->IsTextNode())
+  auto* text_node = DynamicTo<Text>(start_container);
+  if (!text_node)
     return nullptr;
 
   const unsigned start_offset =
@@ -467,8 +470,8 @@
   const unsigned end_offset =
       range.EndPosition().ComputeOffsetInContainerNode();
 
-  return FirstMarkerIntersectingOffsetRange(ToText(*start_container),
-                                            start_offset, end_offset, types);
+  return FirstMarkerIntersectingOffsetRange(*text_node, start_offset,
+                                            end_offset, types);
 }
 
 DocumentMarker* DocumentMarkerController::FirstMarkerIntersectingOffsetRange(
@@ -524,9 +527,10 @@
       range.EndPosition().ComputeOffsetInContainerNode();
 
   for (Node& node : range.Nodes()) {
-    if (!node.IsTextNode())
+    auto* text_node = DynamicTo<Text>(node);
+    if (!text_node)
       continue;
-    MarkerLists* const markers = markers_.at(&ToText(node));
+    MarkerLists* const markers = markers_.at(text_node);
     if (!markers)
       continue;
 
@@ -551,7 +555,7 @@
       const DocumentMarkerVector& markers_from_this_list =
           list->MarkersIntersectingRange(start_offset, end_offset);
       for (DocumentMarker* marker : markers_from_this_list)
-        node_marker_pairs.push_back(std::make_pair(&ToText(node), marker));
+        node_marker_pairs.push_back(std::make_pair(&To<Text>(node), marker));
     }
   }
 
@@ -925,12 +929,13 @@
 
   bool marker_found = false;
   for (Node& node : range.Nodes()) {
-    if (!node.IsTextNode())
+    auto* text_node = DynamicTo<Text>(node);
+    if (!text_node)
       continue;
     int start_offset = node == start_container ? container_start_offset : 0;
     int end_offset = node == end_container ? container_end_offset : INT_MAX;
-    marker_found |= SetTextMatchMarkersActive(ToText(node), start_offset,
-                                              end_offset, active);
+    marker_found |=
+        SetTextMatchMarkersActive(*text_node, start_offset, end_offset, active);
   }
   return marker_found;
 }
@@ -996,9 +1001,10 @@
   if (!PossiblyHasMarkers(DocumentMarker::MarkerTypes::All()))
     return;
   DCHECK(!markers_.IsEmpty());
-  if (!node->IsTextNode())
+  auto* text_node = DynamicTo<Text>(node);
+  if (!text_node)
     return;
-  MarkerLists* markers = markers_.at(ToText(node));
+  MarkerLists* markers = markers_.at(text_node);
   if (!markers)
     return;
 
@@ -1015,7 +1021,7 @@
     return;
   if (!node->GetLayoutObject())
     return;
-  InvalidateRectsForTextMatchMarkersInNode(ToText(*node));
+  InvalidateRectsForTextMatchMarkersInNode(*text_node);
   InvalidatePaintForNode(*node);
 }
 
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc
index 9478047..b86a0d3 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller_test.cc
@@ -373,7 +373,8 @@
 
   ASSERT_EQ(1u, MarkerController().Markers().size());
   auto* marker = To<SuggestionMarker>(MarkerController().Markers()[0].Get());
-  MarkerController().RemoveSuggestionMarkerByTag(*ToText(text), marker->Tag());
+  MarkerController().RemoveSuggestionMarkerByTag(*To<Text>(text),
+                                                 marker->Tag());
   EXPECT_EQ(0u, MarkerController().Markers().size());
 }
 
@@ -397,7 +398,8 @@
 
   const auto* marker =
       To<SuggestionMarker>(MarkerController().Markers()[0].Get());
-  MarkerController().RemoveSuggestionMarkerByTag(*ToText(text), marker->Tag());
+  MarkerController().RemoveSuggestionMarkerByTag(*To<Text>(text),
+                                                 marker->Tag());
   ASSERT_EQ(0u, MarkerController().Markers().size());
 
   // Add a suggestion marker which need to be removed after finish composing,
@@ -420,7 +422,7 @@
   SetBodyContent("<div contenteditable>123456789</div>");
   GetDocument().UpdateStyleAndLayout();
   Element* div = GetDocument().QuerySelector("div");
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
 
   // Add a spelling marker on "123"
   MarkerController().AddSpellingMarker(
@@ -441,7 +443,7 @@
   SetBodyContent("<div contenteditable>123456789</div>");
   GetDocument().UpdateStyleAndLayout();
   Element* div = GetDocument().QuerySelector("div");
-  Text* text = ToText(div->firstChild());
+  auto* text = To<Text>(div->firstChild());
 
   // Add a spelling marker on "123"
   MarkerController().AddSpellingMarker(
diff --git a/third_party/blink/renderer/core/editing/selection_editor.cc b/third_party/blink/renderer/core/editing/selection_editor.cc
index 63349454..a79885a1 100644
--- a/third_party/blink/renderer/core/editing/selection_editor.cc
+++ b/third_party/blink/renderer/core/editing/selection_editor.cc
@@ -344,7 +344,8 @@
   unsigned old_length = old_node.length();
   if (position_offset <= old_length)
     return position;
-  return Position(ToText(old_node.nextSibling()), position_offset - old_length);
+  return Position(To<Text>(old_node.nextSibling()),
+                  position_offset - old_length);
 }
 
 void SelectionEditor::DidSplitTextNode(const Text& old_node) {
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
index 15ca52a..91a4955e 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -153,7 +153,7 @@
 void MarkupAccumulator::AppendStartMarkup(const Node& node) {
   switch (node.getNodeType()) {
     case Node::kTextNode:
-      formatter_.AppendText(markup_, ToText(node));
+      formatter_.AppendText(markup_, To<Text>(node));
       break;
     case Node::kElementNode:
       NOTREACHED();
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 89f7d6d5..e9b7a33 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -757,11 +757,10 @@
 
 void MergeWithNextTextNode(Text* text_node, ExceptionState& exception_state) {
   DCHECK(text_node);
-  Node* next = text_node->nextSibling();
-  if (!next || !next->IsTextNode())
+  auto* text_next = DynamicTo<Text>(text_node->nextSibling());
+  if (!text_next)
     return;
 
-  Text* text_next = ToText(next);
   text_node->appendData(text_next->data());
   if (text_next->parentNode())  // Might have been removed by mutation event.
     text_next->remove(exception_state);
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 9074d30..ed12147 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -55,6 +55,7 @@
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_queue.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
@@ -369,6 +370,10 @@
   histogram.Count(static_cast<int>(reason));
 }
 
+bool IsValidPlaybackRate(double rate) {
+  return rate == 0.0 || (rate > kMinRate && rate < kMaxRate);
+}
+
 }  // anonymous namespace
 
 MIMETypeRegistry::SupportsType HTMLMediaElement::GetSupportsType(
@@ -2236,7 +2241,7 @@
   if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream)
     return;
 
-  if (default_playback_rate_ == rate)
+  if (default_playback_rate_ == rate || !IsValidPlaybackRate(rate))
     return;
 
   default_playback_rate_ = rate;
@@ -2255,7 +2260,7 @@
   if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream)
     return;
 
-  if (rate != 0.0 && (rate < kMinRate || rate > kMaxRate)) {
+  if (!IsValidPlaybackRate(rate)) {
     UseCounter::Count(GetDocument(),
                       WebFeature::kHTMLMediaElementMediaPlaybackRateOutOfRange);
 
@@ -4066,6 +4071,20 @@
   PauseInternal();
 }
 
+void HTMLMediaElement::DefaultEventHandler(Event& event) {
+  if (event.IsKeyboardEvent() && ShouldShowControls()) {
+    const String& key = ToKeyboardEvent(event).key();
+    if (key == "SoftRight") {
+      // We need to handle the event here rather than in
+      // MediaControlsTouchlessImpl because right soft key
+      // event is not sent to JS.
+      GetMediaControls()->ShowContextMenu();
+      event.SetDefaultHandled();
+    }
+  }
+  HTMLElement::DefaultEventHandler(event);
+}
+
 void HTMLMediaElement::AudioSourceProviderImpl::Wrap(
     WebAudioSourceProvider* provider) {
   MutexLocker locker(provide_input_lock);
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 1397814..5785932 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -553,6 +553,8 @@
 
   void OnRemovedFromDocumentTimerFired(TimerBase*);
 
+  void DefaultEventHandler(Event&) override;
+
   TaskRunnerTimer<HTMLMediaElement> load_timer_;
   TaskRunnerTimer<HTMLMediaElement> progress_event_timer_;
   TaskRunnerTimer<HTMLMediaElement> playback_progress_timer_;
diff --git a/third_party/blink/renderer/core/html/media/media_controls.h b/third_party/blink/renderer/core/html/media/media_controls.h
index 57c9b422..057adfa 100644
--- a/third_party/blink/renderer/core/html/media/media_controls.h
+++ b/third_party/blink/renderer/core/html/media/media_controls.h
@@ -77,6 +77,10 @@
   virtual HTMLDivElement* PanelElement() = 0;
   virtual void OnMediaControlsEnabledChange() = 0;
 
+  // This is required for showing a context menu upon pressing right soft key
+  // on a touchless device.
+  virtual void ShowContextMenu() = 0;
+
   void Trace(Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 9ff24c3..7e6a637e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -808,12 +808,12 @@
       CurrentTimeTicksInSeconds(), CurrentTime(), std::move(initiator_object),
       BuildObjectForResourceResponse(redirect_response), resource_type,
       std::move(maybe_frame_id), request.HasUserGesture());
+  if (is_handling_sync_xhr_)
+    GetFrontend()->flush();
 
   if (pending_xhr_replay_data_) {
     resources_data_->SetXHRReplayData(request_id,
                                       pending_xhr_replay_data_.Get());
-    if (!pending_xhr_replay_data_->Async())
-      GetFrontend()->flush();
     pending_xhr_replay_data_.Clear();
   }
   pending_request_type_ = base::nullopt;
@@ -1057,6 +1057,7 @@
   if (monotonic_finish_time.is_null())
     monotonic_finish_time = CurrentTimeTicks();
 
+  is_handling_sync_xhr_ = false;
   // TODO(npm): Use TimeTicks in Network.h.
   GetFrontend()->loadingFinished(
       request_id, monotonic_finish_time.since_origin().InSecondsF(),
@@ -1086,6 +1087,7 @@
     blocked_reason =
         BuildBlockedReason(resource_request_blocked_reason.value());
   }
+  is_handling_sync_xhr_ = false;
   GetFrontend()->loadingFailed(
       request_id, CurrentTimeTicksInSeconds(),
       InspectorPageAgent::ResourceTypeJson(
@@ -1124,6 +1126,9 @@
       form_data ? form_data->DeepCopy() : nullptr, include_credentials);
   for (const auto& header : headers)
     pending_xhr_replay_data_->AddHeader(header.key, header.value);
+  DCHECK(!is_handling_sync_xhr_);
+  if (!async)
+    is_handling_sync_xhr_ = true;
 }
 
 void InspectorNetworkAgent::DidFinishXHR(XMLHttpRequest* xhr) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 71244f7..6be907a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -277,6 +277,7 @@
   base::Optional<InspectorPageAgent::ResourceType> pending_request_type_;
 
   Member<XHRReplayData> pending_xhr_replay_data_;
+  bool is_handling_sync_xhr_ = false;
 
   HashMap<String, std::unique_ptr<protocol::Network::Initiator>>
       frame_navigation_initiator_map_;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 2012c88..e6f881d 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2318,7 +2318,8 @@
 
 scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult(
     const NGConstraintSpace& new_space,
-    const NGBreakToken* break_token) {
+    const NGBreakToken* break_token,
+    base::Optional<NGFragmentGeometry>* initial_fragment_geometry) {
   if (!RuntimeEnabledFeatures::LayoutNGFragmentCachingEnabled())
     return nullptr;
 
@@ -2343,21 +2344,9 @@
     return nullptr;
 
   NGBlockNode node(this);
-  if (!MaySkipLayout(node, *cached_layout_result, new_space))
+  if (!MaySkipLayout(node, *cached_layout_result, new_space,
+                     initial_fragment_geometry))
     return nullptr;
-  // It is possible that our intrinsic size has changed; check for that here.
-  // TODO(cbiesinger): Move this to ::MaySkipLayout.
-  if (new_space.IsShrinkToFit() || NeedMinMaxSize(StyleRef())) {
-    NGBoxFragment fragment(
-        new_space.GetWritingMode(), StyleRef().Direction(),
-        To<NGPhysicalBoxFragment>(*cached_layout_result->PhysicalFragment()));
-    // If we get here, we know that border and padding haven't changed.
-    NGBoxStrut border_padding = fragment.Borders() + fragment.Padding();
-    LayoutUnit size =
-        ComputeInlineSizeForFragment(new_space, node, border_padding);
-    if (size != fragment.InlineSize())
-      return nullptr;
-  }
 
   const NGConstraintSpace& old_space =
       cached_layout_result->GetConstraintSpaceForCaching();
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 1bc47aa..e723b72 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -43,6 +43,7 @@
 class ShapeOutsideInfo;
 struct BoxLayoutExtraInput;
 class NGBreakToken;
+struct NGFragmentGeometry;
 class NGLayoutResult;
 struct NGPhysicalBoxStrut;
 struct PaintInfo;
@@ -913,9 +914,15 @@
   // Returns the last layout result for this block flow with the given
   // constraint space and break token, or null if it is not up-to-date or
   // otherwise unavailable.
+  //
+  // This method (while determining if the layout result can be reused), *may*
+  // calculate the |initial_fragment_geometry| of the node.
+  //
+  // TODO(ikilpatrick): Move this function into NGBlockNode.
   scoped_refptr<const NGLayoutResult> CachedLayoutResult(
       const NGConstraintSpace&,
-      const NGBreakToken*);
+      const NGBreakToken*,
+      base::Optional<NGFragmentGeometry>* initial_fragment_geometry);
 
   void SetSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder&);
   void ClearSpannerPlaceholder();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 8d66c3c..4ad6699 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -28,7 +28,7 @@
   has_last_resort_break_ = false;
   has_floating_descendants_ = false;
   has_orthogonal_flow_roots_ = false;
-  has_child_that_depends_on_percentage_block_size_ = false;
+  has_descendant_that_depends_on_percentage_block_size_ = false;
   has_block_fragmentation_ = false;
   may_have_descendant_above_block_start_ = false;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
index 8305e292..5739245 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -68,8 +68,9 @@
   scoped_refptr<const NGLayoutResult> RunCachedLayoutResult(
       const NGConstraintSpace& space,
       const NGBlockNode& node) {
+    base::Optional<NGFragmentGeometry> initial_fragment_geometry;
     return To<LayoutBlockFlow>(node.GetLayoutBox())
-        ->CachedLayoutResult(space, nullptr);
+        ->CachedLayoutResult(space, nullptr, &initial_fragment_geometry);
   }
 
   String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 9186d43..3b3eca6 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -232,8 +232,9 @@
         previous_result->GetConstraintSpaceForCaching().ExclusionSpace());
   }
 
-  scoped_refptr<const NGLayoutResult> layout_result =
-      box_->CachedLayoutResult(constraint_space, break_token);
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  scoped_refptr<const NGLayoutResult> layout_result = box_->CachedLayoutResult(
+      constraint_space, break_token, &fragment_geometry);
   if (layout_result) {
     // We may have to update the margins on box_; we reuse the layout result
     // even if a percentage margin may have changed.
@@ -248,17 +249,20 @@
     return layout_result;
   }
 
+  if (!fragment_geometry) {
+    fragment_geometry =
+        CalculateInitialFragmentGeometry(constraint_space, *this);
+  }
+
   PrepareForLayout();
 
-  NGFragmentGeometry fragment_geometry =
-      CalculateInitialFragmentGeometry(constraint_space, *this);
-  NGLayoutAlgorithmParams params(*this, fragment_geometry, constraint_space,
+  NGLayoutAlgorithmParams params(*this, *fragment_geometry, constraint_space,
                                  To<NGBlockBreakToken>(break_token));
   layout_result = LayoutWithAlgorithm(params);
   FinishLayout(block_flow, constraint_space, break_token, layout_result);
 
   NGBoxStrut after_layout_scrollbars = GetScrollbarSizes();
-  if (fragment_geometry.scrollbar != after_layout_scrollbars) {
+  if (fragment_geometry->scrollbar != after_layout_scrollbars) {
     // If our scrollbars have changed, we need to relayout because either:
     // - Our size has changed (if shrinking to fit), or
     // - Space available to our children has changed.
@@ -898,7 +902,8 @@
   // We need to force a layout on the child if the constraint space given will
   // change the layout.
   bool needs_force_relayout =
-      layout_result && !MaySkipLayout(*this, *layout_result, constraint_space);
+      layout_result &&
+      !MaySkipLegacyLayout(*this, *layout_result, constraint_space);
 
   if (box_->NeedsLayout() || !layout_result || needs_force_relayout) {
     BoxLayoutExtraInput input(*box_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index c635639..1be6878c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -61,6 +61,7 @@
       const NGFragmentGeometry& initial_fragment_geometry) {
     initial_fragment_geometry_ = &initial_fragment_geometry;
     size_ = initial_fragment_geometry_->border_box_size;
+    is_initial_block_size_indefinite_ = size_.block_size == kIndefiniteSize;
     return *this;
   }
 
@@ -237,6 +238,7 @@
 
   NGPhysicalFragment::NGBoxType box_type_;
   bool is_fieldset_container_ = false;
+  bool is_initial_block_size_indefinite_ = false;
   bool did_break_;
   bool has_forced_break_ = false;
   bool is_new_fc_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
index ef77aa0..410c459 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -60,19 +60,16 @@
  public:
   enum ConstraintSpaceFlags {
     kOrthogonalWritingModeRoot = 1 << 0,
-    kFixedSizeInline = 1 << 1,
-    kFixedSizeBlock = 1 << 2,
-    kFixedSizeBlockIsDefinite = 1 << 3,
-    kShrinkToFit = 1 << 4,
-    kIntermediateLayout = 1 << 5,
-    kSeparateLeadingFragmentainerMargins = 1 << 6,
-    kNewFormattingContext = 1 << 7,
-    kAnonymous = 1 << 8,
-    kUseFirstLineStyle = 1 << 9,
-    kForceClearance = 1 << 10,
+    kFixedSizeBlockIsDefinite = 1 << 1,
+    kIntermediateLayout = 1 << 2,
+    kSeparateLeadingFragmentainerMargins = 1 << 3,
+    kNewFormattingContext = 1 << 4,
+    kAnonymous = 1 << 5,
+    kUseFirstLineStyle = 1 << 6,
+    kForceClearance = 1 << 7,
 
     // Size of bitfield used to store the flags.
-    kNumberOfConstraintSpaceFlags = 11
+    kNumberOfConstraintSpaceFlags = 8
   };
 
   // To ensure that the bfc_offset_, rare_data_ union doesn't get polluted,
@@ -286,9 +283,9 @@
   //
   // If these flags are true, the AvailableSize() is interpreted as the fixed
   // border-box size of this box in the respective dimension.
-  bool IsFixedSizeInline() const { return HasFlag(kFixedSizeInline); }
+  bool IsFixedSizeInline() const { return bitfields_.is_fixed_size_inline; }
 
-  bool IsFixedSizeBlock() const { return HasFlag(kFixedSizeBlock); }
+  bool IsFixedSizeBlock() const { return bitfields_.is_fixed_size_block; }
 
   // Whether a fixed block size should be considered definite.
   bool FixedSizeBlockIsDefinite() const {
@@ -297,7 +294,7 @@
 
   // Whether an auto inline-size should be interpreted as shrink-to-fit
   // (ie. fit-content). This is used for inline-block, floats, etc.
-  bool IsShrinkToFit() const { return HasFlag(kShrinkToFit); }
+  bool IsShrinkToFit() const { return bitfields_.is_shrink_to_fit; }
 
   // Whether this constraint space is used for an intermediate layout in a
   // multi-pass layout. In such a case, we should not copy back the resulting
@@ -436,6 +433,12 @@
     return other.rare_data_->IsInitialForMaySkipLayout();
   }
 
+  // Returns true if the size constraints (shrink-to-fit, fixed-inline-size)
+  // are equal.
+  bool AreSizeConstraintsEqual(const NGConstraintSpace& other) const {
+    return bitfields_.AreSizeConstraintsEqual(other.bitfields_);
+  }
+
   bool AreSizesEqual(const NGConstraintSpace& other) const {
     if (available_size_ != other.available_size_)
       return false;
@@ -553,29 +556,44 @@
 
     explicit Bitfields(WritingMode writing_mode)
         : has_rare_data(false),
-          table_cell_child_layout_phase(static_cast<unsigned>(
-              NGTableCellChildLayoutPhase::kNotTableCellChild)),
           adjoining_floats(static_cast<unsigned>(kFloatTypeNone)),
           writing_mode(static_cast<unsigned>(writing_mode)),
           direction(static_cast<unsigned>(TextDirection::kLtr)),
+          is_shrink_to_fit(false),
+          is_fixed_size_inline(false),
+          is_fixed_size_block(false),
+          table_cell_child_layout_phase(static_cast<unsigned>(
+              NGTableCellChildLayoutPhase::kNotTableCellChild)),
           flags(kFixedSizeBlockIsDefinite),
           percentage_inline_storage(kSameAsAvailable),
           percentage_block_storage(kSameAsAvailable),
           replaced_percentage_block_storage(kSameAsAvailable) {}
 
     bool MaySkipLayout(const Bitfields& other) const {
-      return table_cell_child_layout_phase ==
-                 other.table_cell_child_layout_phase &&
-             adjoining_floats == other.adjoining_floats &&
+      return adjoining_floats == other.adjoining_floats &&
              writing_mode == other.writing_mode && flags == other.flags &&
              baseline_requests == other.baseline_requests;
     }
 
+    bool AreSizeConstraintsEqual(const Bitfields& other) const {
+      return is_shrink_to_fit == other.is_shrink_to_fit &&
+             is_fixed_size_inline == other.is_fixed_size_inline &&
+             is_fixed_size_block == other.is_fixed_size_block &&
+             table_cell_child_layout_phase ==
+                 other.table_cell_child_layout_phase;
+    }
+
     unsigned has_rare_data : 1;
-    unsigned table_cell_child_layout_phase : 2;  // NGTableCellChildLayoutPhase
     unsigned adjoining_floats : 2;               // NGFloatTypes
     unsigned writing_mode : 3;
     unsigned direction : 1;
+
+    // Size constraints.
+    unsigned is_shrink_to_fit : 1;
+    unsigned is_fixed_size_inline : 1;
+    unsigned is_fixed_size_block : 1;
+    unsigned table_cell_child_layout_phase : 2;  // NGTableCellChildLayoutPhase
+
     unsigned flags : kNumberOfConstraintSpaceFlags;  // ConstraintSpaceFlags
     unsigned baseline_requests : NGBaselineRequestList::kSerializedBits;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
index 0274906..676a5340 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -123,18 +123,18 @@
 
   NGConstraintSpaceBuilder& SetIsFixedSizeInline(bool b) {
     if (LIKELY(is_in_parallel_flow_))
-      SetFlag(NGConstraintSpace::kFixedSizeInline, b);
+      space_.bitfields_.is_fixed_size_inline = b;
     else
-      SetFlag(NGConstraintSpace::kFixedSizeBlock, b);
+      space_.bitfields_.is_fixed_size_block = b;
 
     return *this;
   }
 
   NGConstraintSpaceBuilder& SetIsFixedSizeBlock(bool b) {
     if (LIKELY(is_in_parallel_flow_))
-      SetFlag(NGConstraintSpace::kFixedSizeBlock, b);
+      space_.bitfields_.is_fixed_size_block = b;
     else
-      SetFlag(NGConstraintSpace::kFixedSizeInline, b);
+      space_.bitfields_.is_fixed_size_inline = b;
 
     return *this;
   }
@@ -147,7 +147,7 @@
   }
 
   NGConstraintSpaceBuilder& SetIsShrinkToFit(bool b) {
-    SetFlag(NGConstraintSpace::kShrinkToFit, b);
+    space_.bitfields_.is_shrink_to_fit = b;
     return *this;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 46d38c2..f6bb83ca9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -83,7 +83,7 @@
   // percentages against the "final" size of their parent.
   if (child.DependsOnPercentageBlockSize() &&
       !child.PhysicalFragment()->IsOutOfFlowPositioned())
-    has_child_that_depends_on_percentage_block_size_ = true;
+    has_descendant_that_depends_on_percentage_block_size_ = true;
 
   if (child.MayHaveDescendantAboveBlockStart() &&
       !child.PhysicalFragment()->IsBlockFormattingContextRoot())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index ab2c77f62..5553bba9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -245,7 +245,7 @@
   bool has_last_resort_break_ = false;
   bool has_floating_descendants_ = false;
   bool has_orthogonal_flow_roots_ = false;
-  bool has_child_that_depends_on_percentage_block_size_ = false;
+  bool has_descendant_that_depends_on_percentage_block_size_ = false;
   bool has_block_fragmentation_ = false;
   bool may_have_descendant_above_block_start_ = false;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
index d2ee13a..7ce4d13 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -97,6 +97,9 @@
   bool IsBody() const { return IsBlock() && box_->IsBody(); }
   bool IsDocumentElement() const { return box_->IsDocumentElement(); }
   bool IsFlexItem() const { return IsBlock() && box_->IsFlexItemIncludingNG(); }
+  bool IsFlexibleBox() const {
+    return IsBlock() && box_->IsFlexibleBoxIncludingNG();
+  }
   bool ShouldBeConsideredAsReplaced() const {
     return box_->ShouldBeConsideredAsReplaced();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 8456c05..efc8e3c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -18,6 +18,8 @@
     scoped_refptr<const NGPhysicalFragment> physical_fragment,
     NGBoxFragmentBuilder* builder)
     : NGLayoutResult(builder, /* cache_space */ true) {
+  is_initial_block_size_indefinite_ =
+      builder->is_initial_block_size_indefinite_;
   intrinsic_block_size_ = builder->intrinsic_block_size_;
   minimal_space_shortage_ = builder->minimal_space_shortage_;
   initial_break_before_ = builder->initial_break_before_;
@@ -41,6 +43,7 @@
     : NGLayoutResult(builder, /* cache_space */ false) {
   adjoining_floats_ = kFloatTypeNone;
   depends_on_percentage_block_size_ = false;
+  has_descendant_that_depends_on_percentage_block_size_ = false;
   status_ = status;
   DCHECK_NE(status, kSuccess)
       << "Use the other constructor for successful layout";
@@ -69,11 +72,15 @@
       has_forced_break_(other.has_forced_break_),
       is_pushed_by_floats_(other.is_pushed_by_floats_),
       adjoining_floats_(other.adjoining_floats_),
+      is_initial_block_size_indefinite_(
+          other.is_initial_block_size_indefinite_),
       has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_),
       may_have_descendant_above_block_start_(
           other.may_have_descendant_above_block_start_),
       depends_on_percentage_block_size_(
           other.depends_on_percentage_block_size_),
+      has_descendant_that_depends_on_percentage_block_size_(
+          other.has_descendant_that_depends_on_percentage_block_size_),
       status_(other.status_) {}
 
 NGLayoutResult::NGLayoutResult(NGContainerFragmentBuilder* builder,
@@ -90,10 +97,13 @@
       has_forced_break_(false),
       is_pushed_by_floats_(builder->is_pushed_by_floats_),
       adjoining_floats_(builder->adjoining_floats_),
+      is_initial_block_size_indefinite_(false),
       has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
       may_have_descendant_above_block_start_(
           builder->may_have_descendant_above_block_start_),
       depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)),
+      has_descendant_that_depends_on_percentage_block_size_(
+          builder->has_descendant_that_depends_on_percentage_block_size_),
       status_(kSuccess) {}
 
 // Define the destructor here, so that we can forward-declare more in the
@@ -105,7 +115,7 @@
   NGLayoutInputNode node = builder.node_;
 
   if (!node || node.IsInline())
-    return builder.has_child_that_depends_on_percentage_block_size_;
+    return builder.has_descendant_that_depends_on_percentage_block_size_;
 
   // NOTE: If an element is OOF positioned, and has top/bottom constraints
   // which are percentage based, this function will return false.
@@ -118,7 +128,7 @@
   // element if it has a percentage block-size however, but this will return
   // the correct result from below.
 
-  if ((builder.has_child_that_depends_on_percentage_block_size_ ||
+  if ((builder.has_descendant_that_depends_on_percentage_block_size_ ||
        builder.is_legacy_layout_root_) &&
       node.UseParentPercentageResolutionBlockSizeForChildren()) {
     // Quirks mode has different %-block-size behaviour, than standards mode.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index ffa5734..21140c3 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -115,12 +115,27 @@
 
   bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
 
+  // Returns true if the initial (pre-layout) block-size of this fragment was
+  // indefinite. (e.g. it has "height: auto").
+  bool IsInitialBlockSizeIndefinite() const {
+    return is_initial_block_size_indefinite_;
+  }
+
   // Returns true if we aren't able to re-use this layout result if the
   // PercentageResolutionBlockSize changes.
   bool DependsOnPercentageBlockSize() const {
     return depends_on_percentage_block_size_;
   }
 
+  // Returns true if there is a descendant that depends on percentage
+  // resolution block-size changes.
+  // Some layout modes (flex-items, table-cells) have more complex child
+  // percentage sizing behaviour (typically when their parent layout forces a
+  // block-size on them).
+  bool HasDescendantThatDependsOnPercentageBlockSize() const {
+    return has_descendant_that_depends_on_percentage_block_size_;
+  }
+
   // Returns true if we have a descendant within this formatting context, which
   // is potentially above our block-start edge.
   bool MayHaveDescendantAboveBlockStart() const {
@@ -219,9 +234,11 @@
   unsigned is_pushed_by_floats_ : 1;
   unsigned adjoining_floats_ : 2;  // NGFloatTypes
 
+  unsigned is_initial_block_size_indefinite_ : 1;
   unsigned has_orthogonal_flow_roots_ : 1;
   unsigned may_have_descendant_above_block_start_ : 1;
   unsigned depends_on_percentage_block_size_ : 1;
+  unsigned has_descendant_that_depends_on_percentage_block_size_ : 1;
 
   unsigned status_ : 1;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
index 2ee0d2b75..23ae3a4 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
 
@@ -42,10 +43,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_NE(result.get(), nullptr);
   EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(50));
@@ -82,10 +84,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_NE(result.get(), nullptr);
   EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(40));
@@ -143,10 +146,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -180,10 +184,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -215,10 +220,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -250,10 +256,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -284,10 +291,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -318,10 +326,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -352,10 +361,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_NE(result.get(), nullptr);
 }
@@ -386,10 +396,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_NE(result.get(), nullptr);
 }
@@ -421,10 +432,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -456,10 +468,11 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_EQ(result.get(), nullptr);
 }
@@ -484,10 +497,244 @@
   auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
   auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
 
+  base::Optional<NGFragmentGeometry> fragment_geometry;
   const NGConstraintSpace& space =
       src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
   scoped_refptr<const NGLayoutResult> result =
-      test->CachedLayoutResult(space, nullptr);
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitPercentageMinWidth) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // min-width calculates to different values, but doesn't change size.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .inflow { width: 100px; min-width: 25%; }
+    </style>
+    <div class="bfc">
+      <div id="test" class="inflow"></div>
+    </div>
+    <div class="bfc" style="width: 200px; height: 200px;">
+      <div id="src" class="inflow"></div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitFixedMinWidth) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // min-width is always larger than the available size.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .inflow { min-width: 300px; }
+    </style>
+    <div class="bfc">
+      <div id="test" class="inflow"></div>
+    </div>
+    <div class="bfc" style="width: 200px; height: 200px;">
+      <div id="src" class="inflow"></div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // We have a shrink-to-fit node, with the min, and max intrinsic sizes being
+  // equal (the available size doesn't affect the final size).
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .shrink { width: fit-content; }
+      .child { width: 250px; }
+    </style>
+    <div class="bfc">
+      <div id="test" class="shrink">
+        <div class="child"></div>
+      </div>
+    </div>
+    <div class="bfc" style="width: 200px; height: 200px;">
+      <div id="src" class="shrink">
+        <div class="child"></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitShrinkToFitDifferentParent) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // The parent "bfc" node changes from shrink-to-fit, to a fixed width. But
+  // these calculate as the same available space to the "test" element.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; }
+      .child { width: 250px; }
+    </style>
+    <div class="bfc" style="width: fit-content; height: 100px;">
+      <div id="test">
+        <div class="child"></div>
+      </div>
+    </div>
+    <div class="bfc" style="width: 250px; height: 100px;">
+      <div id="src">
+        <div class="child"></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, MissQuirksModePercentageBasedChild) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // Quirks-mode %-block-size child.
+  GetDocument().SetCompatibilityMode(Document::kQuirksMode);
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .child { height: 50%; }
+    </style>
+    <div class="bfc">
+      <div id="test">
+        <div class="child"></div>
+      </div>
+    </div>
+    <div class="bfc" style="height: 200px;">
+      <div id="src">
+        <div class="child"></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_EQ(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitQuirksModePercentageBasedParentAndChild) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // Quirks-mode %-block-size parent *and* child. Here we mark the parent as
+  // depending on %-block-size changes, however itself doesn't change in
+  // height.
+  // We are able to hit the cache as we detect that the height for the child
+  // *isn't* indefinite, and results in the same height as before.
+  GetDocument().SetCompatibilityMode(Document::kQuirksMode);
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .parent { height: 50%; min-height: 200px; }
+      .child { height: 50%; }
+    </style>
+    <div class="bfc">
+      <div id="test" class="parent">
+        <div class="child"></div>
+      </div>
+    </div>
+    <div class="bfc" style="height: 200px;">
+      <div id="src" class="parent">
+        <div class="child"></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
+
+  EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitStandardsModePercentageBasedChild) {
+  ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+  // Standards-mode %-block-size child.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .bfc { display: flow-root; width: 300px; height: 300px; }
+      .child { height: 50%; }
+    </style>
+    <div class="bfc">
+      <div id="test">
+        <div class="child"></div>
+      </div>
+    </div>
+    <div class="bfc" style="height: 200px;">
+      <div id="src">
+        <div class="child"></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+  auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+  base::Optional<NGFragmentGeometry> fragment_geometry;
+  const NGConstraintSpace& space =
+      src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+  scoped_refptr<const NGLayoutResult> result =
+      test->CachedLayoutResult(space, nullptr, &fragment_geometry);
 
   EXPECT_NE(result.get(), nullptr);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
index fce051a0d..78c7aa7f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
@@ -4,15 +4,23 @@
 
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
 
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
 
 namespace {
 
+// LengthResolveType indicates what type length the function is being passed
+// based on its CSS property. E.g.
+// kMinSize - min-width / min-height
+// kMaxSize - max-width / max-height
+// kMainSize - width / height
+enum class LengthResolveType { kMinSize, kMaxSize, kMainSize };
+
 bool ContentShrinkToFitMayChange(const ComputedStyle& style,
                                  const NGConstraintSpace& new_space,
                                  const NGConstraintSpace& old_space,
@@ -100,6 +108,8 @@
       type == LengthResolveType::kMainSize &&
       (new_space.IsShrinkToFit() || length.IsFitContent());
 
+  // TODO(ikilpatrick): Test if we can remove this optimization now that we
+  // compute the initial size of the fragment.
   if (is_content_shrink_to_fit) {
     return ContentShrinkToFitMayChange(style, new_space, old_space,
                                        layout_result);
@@ -127,18 +137,27 @@
         old_space.AvailableSize().block_size)
       return true;
   }
+
   return false;
 }
 
 // Return true if it's possible (but not necessarily guaranteed) that the new
 // constraint space will give a different size compared to the old one, when
 // computed style and child content remain unchanged.
-bool SizeMayChange(const ComputedStyle& style,
+bool SizeMayChange(const NGBlockNode& node,
                    const NGConstraintSpace& new_space,
                    const NGConstraintSpace& old_space,
                    const NGLayoutResult& layout_result) {
+  if (node.IsQuirkyAndFillsViewport())
+    return true;
+
   DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
   DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
+  DCHECK_EQ(new_space.IsShrinkToFit(), old_space.IsShrinkToFit());
+  DCHECK_EQ(new_space.TableCellChildLayoutPhase(),
+            old_space.TableCellChildLayoutPhase());
+
+  const ComputedStyle& style = node.Style();
 
   // Go through all length properties, and, depending on length type
   // (percentages, auto, etc.), check whether the constraint spaces differ in
@@ -203,11 +222,129 @@
   return false;
 }
 
+// Return true if the new constraint space will produce a different sized
+// fragment. This will also return true if any %-block-size children will
+// change size.
+bool SizeWillChange(const NGBlockNode& node,
+                    const NGFragmentGeometry& fragment_geometry,
+                    const NGLayoutResult& layout_result,
+                    const NGConstraintSpace& new_space,
+                    const NGConstraintSpace& old_space) {
+  const ComputedStyle& style = node.Style();
+  NGBoxFragment fragment(
+      style.GetWritingMode(), style.Direction(),
+      To<NGPhysicalBoxFragment>(*layout_result.PhysicalFragment()));
+
+  if (fragment_geometry.border_box_size.inline_size != fragment.InlineSize())
+    return true;
+
+  LayoutUnit block_size = fragment_geometry.border_box_size.block_size;
+  bool is_initial_block_size_indefinite = block_size == kIndefiniteSize;
+  if (is_initial_block_size_indefinite) {
+    // The intrinsic size of column flex-boxes can depend on the
+    // %-resolution-block-size. This occurs when a flex-box has "max-height:
+    // 100%" or similar on itself.
+    //
+    // Due to this we can't use cached |NGLayoutResult::IntrinsicBlockSize|
+    // value, as the following |block_size| calculation would be incorrect.
+    if (node.IsFlexibleBox() && style.IsColumnFlexDirection() &&
+        layout_result.DependsOnPercentageBlockSize()) {
+      if (new_space.PercentageResolutionBlockSize() !=
+          old_space.PercentageResolutionBlockSize())
+        return true;
+    }
+
+    block_size = ComputeBlockSizeForFragment(
+        new_space, style, fragment_geometry.border + fragment_geometry.padding,
+        layout_result.IntrinsicBlockSize());
+  }
+
+  if (block_size != fragment.BlockSize())
+    return true;
+
+  if (layout_result.HasDescendantThatDependsOnPercentageBlockSize()) {
+    // %-block-size children of flex-items sometimes don't resolve their
+    // percentages against a fixed block-size.
+    // We miss the cache if the %-resolution block-size changes from indefinite
+    // to definite (or visa-versa).
+    bool is_new_initial_block_size_indefinite =
+        new_space.IsFixedSizeBlock() ? !new_space.FixedSizeBlockIsDefinite()
+                                     : is_initial_block_size_indefinite;
+
+    bool is_old_initial_block_size_indefinite =
+        old_space.IsFixedSizeBlock()
+            ? !old_space.FixedSizeBlockIsDefinite()
+            : layout_result.IsInitialBlockSizeIndefinite();
+
+    if (is_old_initial_block_size_indefinite !=
+        is_new_initial_block_size_indefinite)
+      return true;
+
+    // %-block-size children of table-cells have different behaviour if they
+    // are in the "measure" or "layout" phase.
+    // Instead of trying to capture that logic here, we always miss the cache.
+    if (node.IsTableCell() &&
+        new_space.IsFixedSizeBlock() != old_space.IsFixedSizeBlock())
+      return true;
+
+    // At this point we must have the same block-size for our fragment, so this
+    // is really only checking if any %-block-size children will change size.
+    // This is checks for the quirks-mode %-block-size behaviour.
+    //
+    // The |NGLayoutResult::DependsOnPercentageBlockSize| flag will returns true
+    // if we are in quirks mode, and have a descendant that depends on a
+    // percentage block-size, however it will also return true if the node
+    // itself depends on the %-block-size.
+    //
+    // We remove this false-positive by checking if we have an initial
+    // indefinite block-size.
+    if (is_new_initial_block_size_indefinite &&
+        layout_result.DependsOnPercentageBlockSize()) {
+      DCHECK(is_old_initial_block_size_indefinite);
+      if (new_space.PercentageResolutionBlockSize() !=
+          old_space.PercentageResolutionBlockSize())
+        return true;
+      if (new_space.ReplacedPercentageResolutionBlockSize() !=
+          old_space.ReplacedPercentageResolutionBlockSize())
+        return true;
+    }
+  }
+
+  if (style.MayHavePadding() && fragment_geometry.padding != fragment.Padding())
+    return true;
+
+  return false;
+}
+
+bool IntrinsicSizeWillChange(
+    const NGBlockNode& node,
+    const NGLayoutResult& cached_layout_result,
+    const NGConstraintSpace& new_space,
+    base::Optional<NGFragmentGeometry>* fragment_geometry) {
+  const ComputedStyle& style = node.Style();
+  if (!new_space.IsShrinkToFit() && !NeedMinMaxSize(style))
+    return false;
+
+  if (!*fragment_geometry)
+    *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node);
+
+  NGBoxFragment fragment(
+      style.GetWritingMode(), style.Direction(),
+      To<NGPhysicalBoxFragment>(*cached_layout_result.PhysicalFragment()));
+
+  if ((*fragment_geometry)->border_box_size.inline_size !=
+      fragment.InlineSize())
+    return true;
+
+  return false;
+}
+
 }  // namespace
 
-bool MaySkipLayout(const NGBlockNode node,
+bool MaySkipLayout(const NGBlockNode& node,
                    const NGLayoutResult& cached_layout_result,
-                   const NGConstraintSpace& new_space) {
+                   const NGConstraintSpace& new_space,
+                   base::Optional<NGFragmentGeometry>* fragment_geometry) {
   DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
   DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
 
@@ -216,18 +353,53 @@
   if (!new_space.MaySkipLayout(old_space))
     return false;
 
-  if (!new_space.AreSizesEqual(old_space)) {
-    // We need to descend all the way down into BODY if we're in quirks mode,
-    // since it magically follows the viewport size.
-    if (node.IsQuirkyAndFillsViewport())
+  if (new_space.AreSizeConstraintsEqual(old_space)) {
+    // It is possible that our intrinsic size has changed, check for that here.
+    // TODO(cbiesinger): Investigate why this check doesn't apply to
+    // |MaySkipLegacyLayout|.
+    if (IntrinsicSizeWillChange(node, cached_layout_result, new_space,
+                                fragment_geometry))
       return false;
 
-    // If the available / percentage sizes have changed in a way that may affect
-    // layout, we cannot re-use the previous result.
-    if (SizeMayChange(node.Style(), new_space, old_space, cached_layout_result))
-      return false;
+    // We don't have to check our style if we know the constraint space sizes
+    // will remain the same.
+    if (new_space.AreSizesEqual(old_space))
+      return true;
+
+    if (!SizeMayChange(node, new_space, old_space, cached_layout_result))
+      return true;
   }
 
+  if (!*fragment_geometry)
+    *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node);
+
+  if (SizeWillChange(node, **fragment_geometry, cached_layout_result, new_space,
+                     old_space))
+    return false;
+
+  return true;
+}
+
+bool MaySkipLegacyLayout(const NGBlockNode& node,
+                         const NGLayoutResult& cached_layout_result,
+                         const NGConstraintSpace& new_space) {
+  DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
+  DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
+
+  const NGConstraintSpace& old_space =
+      cached_layout_result.GetConstraintSpaceForCaching();
+  if (!new_space.MaySkipLayout(old_space))
+    return false;
+
+  if (!new_space.AreSizeConstraintsEqual(old_space))
+    return false;
+
+  if (new_space.AreSizesEqual(old_space))
+    return true;
+
+  if (SizeMayChange(node, new_space, old_space, cached_layout_result))
+    return false;
+
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
index 1a880d9..486cddb8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
@@ -15,9 +15,16 @@
 // Returns true if for a given |new_space|, the |node| will provide the same
 // |NGLayoutResult| as |cached_layout_result|, and therefore might be able to
 // skip layout.
-bool MaySkipLayout(const NGBlockNode node,
+bool MaySkipLayout(const NGBlockNode& node,
                    const NGLayoutResult& cached_layout_result,
-                   const NGConstraintSpace& new_space);
+                   const NGConstraintSpace& new_space,
+                   base::Optional<NGFragmentGeometry>* fragment_geometry);
+
+// Similar to |MaySkipLayout| but for legacy layout roots. Doesn't attempt to
+// pre-compute the geometry of the fragment.
+bool MaySkipLegacyLayout(const NGBlockNode& node,
+                         const NGLayoutResult& cached_layout_result,
+                         const NGConstraintSpace& new_space);
 
 // Return true if layout is considered complete. In some cases we require more
 // than one layout pass.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
index fefcd6c..07ab2f59 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -31,13 +31,6 @@
 // intrinsic sizes pass, and kLayout must be used during the layout pass.
 enum class LengthResolvePhase { kIntrinsic, kLayout };
 
-// LengthResolveType indicates what type length the function is being passed
-// based on its CSS property. E.g.
-// kMinSize - min-width / min-height
-// kMaxSize - max-width / max-height
-// kMainSize - width / height
-enum class LengthResolveType { kMinSize, kMaxSize, kMainSize };
-
 inline bool NeedMinMaxSize(const ComputedStyle& style) {
   // This check is technically too broad (fill-available does not need intrinsic
   // size computation) but that's a rare case and only affects performance, not
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
index c660d99..36f8f881 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
@@ -119,6 +119,7 @@
     // There is no update because only the overlay is expected to change.
     RefreshCastButtonVisibilityWithoutUpdate();
   }
+  void ShowContextMenu() override {}
 
   // Called by the fullscreen buttons to toggle fulllscreen on/off.
   void EnterFullscreen();
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
index 3efbc4c..4542382 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
@@ -16,19 +16,31 @@
   SetShadowPseudoId(
       AtomicString("-internal-media-controls-touchless-bottom-container"));
 
-  MediaControlsTouchlessTimeDisplayElement* time_display_element =
+  time_display_element_ =
       MakeGarbageCollected<MediaControlsTouchlessTimeDisplayElement>(
           media_controls);
-  MediaControlsTouchlessTimelineElement* timeline_element =
+  timeline_element_ =
       MakeGarbageCollected<MediaControlsTouchlessTimelineElement>(
           media_controls);
 
-  ParserAppendChild(time_display_element);
-  ParserAppendChild(timeline_element);
+  ParserAppendChild(time_display_element_);
+  ParserAppendChild(timeline_element_);
+}
+
+LayoutObject*
+MediaControlsTouchlessBottomContainerElement::TimelineLayoutObject() {
+  return timeline_element_->GetLayoutObject();
+}
+
+LayoutObject*
+MediaControlsTouchlessBottomContainerElement::TimeDisplayLayoutObject() {
+  return time_display_element_->GetLayoutObject();
 }
 
 void MediaControlsTouchlessBottomContainerElement::Trace(
     blink::Visitor* visitor) {
+  visitor->Trace(timeline_element_);
+  visitor->Trace(time_display_element_);
   MediaControlsTouchlessElement::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h
index bfe01bc..34d04de 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h
@@ -10,12 +10,21 @@
 namespace blink {
 
 class MediaControlsTouchlessImpl;
+class MediaControlsTouchlessTimelineElement;
+class MediaControlsTouchlessTimeDisplayElement;
+class LayoutObject;
 
 class MediaControlsTouchlessBottomContainerElement
     : public MediaControlsTouchlessElement {
  public:
   MediaControlsTouchlessBottomContainerElement(MediaControlsTouchlessImpl&);
+  LayoutObject* TimelineLayoutObject();
+  LayoutObject* TimeDisplayLayoutObject();
   void Trace(blink::Visitor*) override;
+
+ private:
+  Member<MediaControlsTouchlessTimelineElement> timeline_element_;
+  Member<MediaControlsTouchlessTimeDisplayElement> time_display_element_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
index f7bb2b8..7a2c2a8 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
@@ -14,6 +14,8 @@
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/local_frame_client.h"
+#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/html/track/text_track.h"
@@ -141,6 +143,22 @@
   SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
 }
 
+LayoutObject* MediaControlsTouchlessImpl::PanelLayoutObject() {
+  return nullptr;
+}
+
+LayoutObject* MediaControlsTouchlessImpl::TimelineLayoutObject() {
+  return bottom_container_->TimelineLayoutObject();
+}
+
+LayoutObject* MediaControlsTouchlessImpl::ButtonPanelLayoutObject() {
+  return bottom_container_->TimeDisplayLayoutObject();
+}
+
+LayoutObject* MediaControlsTouchlessImpl::ContainerLayoutObject() {
+  return GetLayoutObject();
+}
+
 MediaControlsTouchlessMediaEventListener&
 MediaControlsTouchlessImpl::MediaEventListener() const {
   return *media_event_listener_;
@@ -272,7 +290,42 @@
 }
 
 void MediaControlsTouchlessImpl::OnMediaMenuResult(
-    mojom::blink::MenuResponsePtr reponse) {}
+    mojom::blink::MenuResponsePtr response) {
+  if (response.is_null())
+    return;
+
+  switch (response->clicked) {
+    case mojom::blink::MenuItem::FULLSCREEN:
+      if (MediaElement().IsFullscreen())
+        Fullscreen::ExitFullscreen(GetDocument());
+      else
+        Fullscreen::RequestFullscreen(MediaElement());
+      break;
+    case mojom::blink::MenuItem::MUTE:
+      MediaElement().setMuted(!MediaElement().muted());
+      break;
+    case mojom::blink::MenuItem::DOWNLOAD:
+      Download();
+      break;
+    case mojom::blink::MenuItem::CAPTIONS:
+      text_track_manager_->DisableShowingTextTracks();
+      if (response->track_index >= 0)
+        text_track_manager_->ShowTextTrackAtIndex(response->track_index);
+      break;
+  }
+}
+
+void MediaControlsTouchlessImpl::Download() {
+  const KURL& url = MediaElement().currentSrc();
+  if (url.IsNull() || url.IsEmpty())
+    return;
+  ResourceRequest request(url);
+  request.SetSuggestedFilename(MediaElement().title());
+  request.SetRequestContext(mojom::RequestContextType::DOWNLOAD);
+  request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url()));
+  GetDocument().GetFrame()->Client()->DownloadURL(
+      request, DownloadCrossOriginRedirects::kFollow);
+}
 
 void MediaControlsTouchlessImpl::OnMediaControlsMenuHostConnectionError() {
   media_controls_host_.reset();
@@ -395,4 +448,9 @@
   HTMLDivElement::Trace(visitor);
 }
 
+void MediaControlsTouchlessImpl::OnMediaMenuResultForTest(
+    mojom::blink::MenuResponsePtr response) {
+  OnMediaMenuResult(std::move(response));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
index 0b13425d..14fc333 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
@@ -43,10 +43,11 @@
   void OnControlsListUpdated() override {}
   void OnTrackElementFailedToLoad() override {}
   void NetworkStateChanged() override {}
-  LayoutObject* PanelLayoutObject() override { return nullptr; }
-  LayoutObject* TimelineLayoutObject() override { return nullptr; }
-  LayoutObject* ButtonPanelLayoutObject() override { return nullptr; }
-  LayoutObject* ContainerLayoutObject() override { return nullptr; }
+  LayoutObject* PanelLayoutObject() override;
+  LayoutObject* TimelineLayoutObject() override;
+  LayoutObject* ButtonPanelLayoutObject() override;
+  LayoutObject* ContainerLayoutObject() override;
+  void ShowContextMenu() override;
   void SetTestMode(bool) override {}
   HTMLDivElement* PanelElement() override { return nullptr; }
   void OnMediaControlsEnabledChange() override {}
@@ -68,6 +69,9 @@
 
   MediaControlsTouchlessMediaEventListener& MediaEventListener() const;
 
+  // Test functions
+  void OnMediaMenuResultForTest(mojom::blink::MenuResponsePtr);
+
   void Trace(blink::Visitor*) override;
 
  private:
@@ -87,13 +91,14 @@
   void MaybeJump(int);
   void MaybeChangeVolume(double);
 
+  void Download();
+
   // Node
   bool IsMediaControls() const override { return true; }
 
   void EnsureMediaControlsMenuHost();
   mojom::blink::VideoStatePtr GetVideoState();
   WTF::Vector<mojom::blink::TextTrackMetadataPtr> GetTextTracks();
-  void ShowContextMenu();
   void OnMediaMenuResult(mojom::blink::MenuResponsePtr);
   void OnMediaControlsMenuHostConnectionError();
 
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
index d3bff22..9215321d 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
@@ -19,6 +19,8 @@
 #include "third_party/blink/renderer/core/html/media/html_media_test_helper.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/html/time_ranges.h"
+#include "third_party/blink/renderer/core/html/track/text_track.h"
+#include "third_party/blink/renderer/core/html/track/text_track_list.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
@@ -124,6 +126,14 @@
     chrome_client_->SetOrientation(orientation_type);
   }
 
+  void SimulateClickOnMenuItem(mojom::blink::MenuItem menu_item,
+                               int track_index) {
+    mojom::blink::MenuResponsePtr response(mojom::blink::MenuResponse::New());
+    response->clicked = menu_item;
+    response->track_index = track_index;
+    media_controls_->OnMediaMenuResultForTest(std::move(response));
+  }
+
   void CheckControlKeys(int seek_forward_key,
                         int seek_backward_key,
                         int volume_up_key,
@@ -315,6 +325,48 @@
               volume_bar_height / volume_bar_background_height, error);
 }
 
+/** (jazzhsu@) TODO: Add mojom binding test and fix the following test.
+TEST_F(MediaControlsTouchlessImplTest, ContextMenuTest) {
+  // Fullscreen buttom test.
+  EXPECT_FALSE(MediaElement().IsFullscreen());
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::FULLSCREEN, -1);
+  test::RunPendingTasks();
+  EXPECT_TRUE(MediaElement().IsFullscreen());
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::FULLSCREEN, -1);
+  test::RunPendingTasks();
+  EXPECT_FALSE(MediaElement().IsFullscreen());
+
+  // Mute buttom test.
+  EXPECT_FALSE(MediaElement().muted());
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::MUTE, -1);
+  EXPECT_TRUE(MediaElement().muted());
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::MUTE, -1);
+  EXPECT_FALSE(MediaElement().muted());
+
+  // Text track test.
+  TextTrack* track1 = MediaElement().addTextTrack("subtitle", "english",
+                                                  "en", NASSERT_NO_EXCEPTION);
+  TextTrack* track2 = MediaElement().addTextTrack("subtitle", "english2",
+                                                  "en", ASSERT_NO_EXCEPTION);
+  EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword());
+  EXPECT_NE(track2->mode(), TextTrack::ShowingKeyword());
+
+  // Select first track.
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, 0);
+  EXPECT_EQ(track1->mode(), TextTrack::ShowingKeyword());
+
+  // Select second track.
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, 1);
+  EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword());
+  EXPECT_EQ(track2->mode(), TextTrack::ShowingKeyword());
+
+  // Turn all tracks off.
+  SimulateClickOnMenuItem(mojom::blink::MenuItem::CAPTIONS, -1);
+  EXPECT_NE(track1->mode(), TextTrack::ShowingKeyword());
+  EXPECT_NE(track2->mode(), TextTrack::ShowingKeyword());
+}
+*/
+
 TEST_F(MediaControlsTouchlessImplTestWithMockScheduler,
        MidOverlayHideTimerTest) {
   Element* overlay =
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
index 4f50c83..1ef89f2 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
+++ b/third_party/blink/renderer/modules/media_controls/touchless/resources/mediaControlsTouchless.css
@@ -213,3 +213,79 @@
 video::-webkit-media-controls-touchless.inline div[pseudo="-internal-media-controls-touchless-volume-icon" i] {
   display: none;
 }
+
+/**
+ * Text Tracks
+ */
+video::-webkit-media-text-track-container {
+    position: relative;
+    width: inherit;
+    height: inherit;
+    overflow: hidden;
+
+    font: 22px sans-serif;
+    text-align: center;
+    color: rgba(255, 255, 255, 1);
+
+    letter-spacing: normal;
+    word-spacing: normal;
+    text-transform: none;
+    text-indent: 0;
+    text-decoration: none;
+    pointer-events: none;
+    -webkit-user-select: none;
+    word-break: break-word;
+}
+
+video::cue {
+    display: inline;
+
+    background-color: rgba(0, 0, 0, 0.8);
+}
+
+video::-webkit-media-text-track-region {
+    position: absolute;
+    line-height: 5.33vh;
+    writing-mode: horizontal-tb;
+    background: rgba(0, 0, 0, 0.8);
+    color: rgba(255, 255, 255, 1);
+    word-wrap: break-word;
+    overflow-wrap: break-word;
+    overflow: hidden;
+}
+
+video::-webkit-media-text-track-region-container {
+    position: relative;
+
+    display: flex;
+    flex-flow: column;
+    flex-direction: column;
+}
+
+video::-webkit-media-text-track-region-container.scrolling {
+    transition: top 433ms linear;
+}
+
+video::-webkit-media-text-track-display {
+    position: absolute;
+    overflow: hidden;
+    white-space: pre-wrap;
+    -webkit-box-sizing: border-box;
+    flex: 0 0 auto;
+}
+
+video::cue(:future) {
+    color: gray;
+}
+
+video::cue(b) {
+    font-weight: bold;
+}
+
+video::cue(u) {
+    text-decoration: underline;
+}
+
+video::cue(i) {
+    font-style: italic;
+}
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
index ff0cd3f..5f24440 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
+++ b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
@@ -26,3 +26,4 @@
 Bug(none) virtual/omt-worker-fetch [ Skip ]
 Bug(none) virtual/outofblink-cors [ Skip ]
 Bug(none) http/tests/inspector-protocol/fetch [ Skip ]
+Bug(none) http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js [ Skip ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 567e9fd7..8730070 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -280,7 +280,7 @@
 crbug.com/591099 fast/css/outline-offset-large.html [ Failure ]
 crbug.com/855279 fast/css/text-overflow-ellipsis-vertical-hittest.html [ Pass ]
 crbug.com/591099 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ Failure ]
-crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass ]
+crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass Timeout ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
@@ -300,7 +300,7 @@
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-023.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/white-space/control-chars-00C.html [ Pass ]
-crbug.com/835484 fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html [ Failure Pass ]
+crbug.com/835484 fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html [ Failure ]
 crbug.com/899902 fast/text/ellipsis-with-self-painting-layer.html [ Pass ]
 crbug.com/591099 fast/text/emoji-vertical-origin-visual.html [ Failure ]
 crbug.com/591099 fast/text/font-format-support-color-cff2-vertical.html [ Failure ]
@@ -313,15 +313,17 @@
 crbug.com/591099 http/tests/appcache/non-html.xhtml [ Crash Pass ]
 crbug.com/591099 http/tests/csspaint/invalidation-border-image.html [ Pass ]
 crbug.com/591099 http/tests/devtools/sources/debugger-frameworks/frameworks-jquery.js [ Crash Pass ]
-crbug.com/591099 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass Timeout ]
+crbug.com/591099 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing-session-id.js [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Pass ]
 crbug.com/591099 http/tests/html/validation-bubble-oopif-clip.html [ Pass ]
-crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure ]
+crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure Pass ]
 crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Pass ]
-crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass Timeout ]
+crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass ]
+crbug.com/591099 http/tests/security/mixedContent/insecure-css-resources.html [ Failure Pass ]
 crbug.com/591099 images/feature-policy-oversized-images-resize.html [ Pass ]
 crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
+crbug.com/591099 paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
 crbug.com/835484 paint/invalidation/outline/inline-focus.html [ Failure ]
 crbug.com/591099 paint/invalidation/overflow/opacity-change-on-overflow-float.html [ Failure ]
 crbug.com/591099 paint/invalidation/scroll/fixed-under-composited-fixed-scrolled.html [ Failure ]
@@ -349,7 +351,7 @@
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass ]
-crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
+crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blend-image.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Pass ]
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt
new file mode 100644
index 0000000..3450bb2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs-expected.txt
@@ -0,0 +1,6 @@
+Tests that Network.requestWillBeSent is dispatched for redirects inside sync XHRs
+Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/initial
+Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/redirect1
+Network.requestWillBeSent: http://127.0.0.1:8000/inspector-protocol/network/redirect2
+sync XHR body: thisisxhrbody
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js
new file mode 100644
index 0000000..3920134
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js
@@ -0,0 +1,59 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Tests that Network.requestWillBeSent is dispatched for redirects inside sync XHRs`);
+
+  await dp.Network.enable();
+  await dp.Fetch.enable();
+
+  dp.Network.onRequestWillBeSent(event => testRunner.log('Network.requestWillBeSent: ' + event.params.request.url));
+
+  const url = testRunner.url('./initial');
+
+  const evaluationPromise = session.evaluate(url => {
+    const request = new XMLHttpRequest();
+    request.open('GET', url, false);  // `false` makes the request synchronous
+    request.send(null);
+    return request.responseText;
+  }, url);
+
+  // Wait for initial request.
+  const [interception1] = await Promise.all([
+    dp.Fetch.onceRequestPaused(),
+    dp.Network.onceRequestWillBeSent(),
+  ]);
+  await dp.Fetch.fulfillRequest({
+    requestId: interception1.params.requestId,
+    responseCode: 302,
+    responseHeaders: [
+      {name: 'Location', value: testRunner.url('./redirect1')},
+    ],
+  });
+
+  // First redirect should emit both Fetch.requestPaused and Network.requestWillBeSent.
+  const [interception2] = await Promise.all([
+    dp.Fetch.onceRequestPaused(),
+    dp.Network.onceRequestWillBeSent(),
+  ]);
+  await dp.Fetch.fulfillRequest({
+    requestId: interception2.params.requestId,
+    responseCode: 302,
+    responseHeaders: [
+      {name: 'Location', value: testRunner.url('./redirect2')},
+    ],
+  });
+
+  // Second redirect should emit both Fetch.requestPaused and Network.requestWillBeSent.
+  const [interception3] = await Promise.all([
+    dp.Fetch.onceRequestPaused(),
+    dp.Network.onceRequestWillBeSent(),
+  ]);
+  await dp.Fetch.fulfillRequest({
+    requestId: interception3.params.requestId,
+    responseCode: 200,
+    responseHeaders: [],
+    body: btoa('thisisxhrbody'),
+  });
+
+  testRunner.log('sync XHR body: ' + (await evaluationPromise));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/media/video-defaultplaybackrate.html b/third_party/blink/web_tests/media/video-defaultplaybackrate.html
new file mode 100644
index 0000000..f28a052
--- /dev/null
+++ b/third_party/blink/web_tests/media/video-defaultplaybackrate.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Test playbackRate and defaultPlaybackRate.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<video></video>
+<script>
+async_test(t => {
+  let video = document.querySelector("video");
+  video.src = "content/test.ogv";
+
+  video.addEventListener('canplaythrough', t.step_func(() => {
+    assert_equals(video.defaultPlaybackRate, 1.0);
+    video.defaultPlaybackRate = Number.MAX_VALUE;
+
+    assert_equals(video.defaultPlaybackRate, 1.0);
+    video.load();
+
+    video.addEventListener('canplaythrough', t.step_func_done(() => {
+      assert_equals(video.defaultPlaybackRate, 1.0);
+    }), { once: true });
+  }), { once: true });
+});
+</script>
diff --git a/third_party/blink/web_tests/media/video-playbackrate.html b/third_party/blink/web_tests/media/video-playbackrate.html
index 922a1a6..db15b09 100644
--- a/third_party/blink/web_tests/media/video-playbackrate.html
+++ b/third_party/blink/web_tests/media/video-playbackrate.html
@@ -28,10 +28,10 @@
         // Test extreme playback rates.
 
         video.defaultPlaybackRate = Number.MIN_VALUE;
-        assert_equals(video.defaultPlaybackRate, Number.MIN_VALUE);
+        assert_equals(video.defaultPlaybackRate, 2);
 
         video.defaultPlaybackRate = Number.MAX_VALUE;
-        assert_equals(video.defaultPlaybackRate, Number.MAX_VALUE);
+        assert_equals(video.defaultPlaybackRate, 2);
 
         assert_throws("NotSupportedError", function() { video.playbackRate = Number.MIN_VALUE; });
         assert_throws("NotSupportedError", function() { video.playbackRate = Number.MAX_VALUE; });
@@ -79,4 +79,4 @@
 
     video.src = "content/test.ogv";
 });
-</script>
\ No newline at end of file
+</script>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ff32836..6137ecb 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32758,6 +32758,8 @@
   <int value="-1385221197"
       label="AllowSignedHTTPExchangeCertsWithoutExtension:enabled"/>
   <int value="-1383597259" label="SyncUserConsentSeparateType:disabled"/>
+  <int value="-1383145700"
+      label="AutofillDoNotMigrateUnsupportedLocalCards:enabled"/>
   <int value="-1382671832" label="OmniboxUIExperimentVerticalMargin:enabled"/>
   <int value="-1377186702" label="DesktopIOSPromotion:disabled"/>
   <int value="-1376510363" label="ServiceWorkerScriptFullCodeCache:disabled"/>
@@ -33400,6 +33402,8 @@
   <int value="-385337473" label="enable-fast-unload"/>
   <int value="-384589459" label="disable-supervised-user-safesites"/>
   <int value="-381181808" label="DragAppsInTabletMode:enabled"/>
+  <int value="-379809954"
+      label="AutofillDoNotMigrateUnsupportedLocalCards:disabled"/>
   <int value="-378218969" label="VaapiJpegImageDecodeAcceleration:disabled"/>
   <int value="-378180863" label="disable-panels"/>
   <int value="-378033324" label="disable-win32k-renderer-lockdown"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ab73af3..7f4f2ea 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -147121,6 +147121,17 @@
       name="Net.QuicSession.ConnectionCloseErrorCodeServerGoogle"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="HandshakeNotConfirmed" separator=".">
+  <suffix name="HandshakeNotConfirmed" label="the handshake was confirmed"/>
+  <affected-histogram name="Net.QuicSession.ClosedByPublicReset"/>
+  <affected-histogram name="Net.QuicSession.ConnectionCloseErrorCodeClient"/>
+  <affected-histogram
+      name="Net.QuicSession.ConnectionCloseErrorCodeClientGoogle"/>
+  <affected-histogram name="Net.QuicSession.ConnectionCloseErrorCodeServer"/>
+  <affected-histogram
+      name="Net.QuicSession.ConnectionCloseErrorCodeServerGoogle"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="HighDownloadBandwidth" separator=".">
   <suffix name="HighDownloadBandwidth" label="download with high bandwidth."/>
   <affected-histogram name="Download.Parallelizable.DownloadTime"/>
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 87241afa..70b5ecd 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -77,6 +77,9 @@
   <message name="IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL" desc="A label for the 'Audio' root of media views.">
     Audio
   </message>
+  <message name="IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL" desc="PluginVm local directory label.">
+    Plugin VM
+  </message>
   <message name="IDS_FILE_BROWSER_RECENT_ROOT_LABEL" desc="A label for the 'Recent' root which shows files recently modified by the user.">
     Recent
   </message>
diff --git a/ui/file_manager/file_manager/common/js/file_type.js b/ui/file_manager/file_manager/common/js/file_type.js
index ba2337b..6f024c5 100644
--- a/ui/file_manager/file_manager/common/js/file_type.js
+++ b/ui/file_manager/file_manager/common/js/file_type.js
@@ -620,6 +620,7 @@
   const overrides = {
     [VolumeManagerCommon.RootType.DOWNLOADS]: {
       '/Downloads': VolumeManagerCommon.VolumeType.DOWNLOADS,
+      '/PluginVm': 'plugin_vm',
     },
   };
   const root = overrides[opt_rootType];
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index ff75ff4..04c5145 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1151,11 +1151,15 @@
     return util.getRootTypeLabel(locationInfo);
   }
 
-  // Special case for MyFiles/Downloads.
-  if (locationInfo && util.isMyFilesVolumeEnabled() &&
-      locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS &&
-      entry.fullPath == '/Downloads') {
-    return str('DOWNLOADS_DIRECTORY_LABEL');
+  // Special case for MyFiles/Downloads and MyFiles/PluginVm.
+  if (locationInfo &&
+      locationInfo.rootType == VolumeManagerCommon.RootType.DOWNLOADS) {
+    if (util.isMyFilesVolumeEnabled() && entry.fullPath == '/Downloads') {
+      return str('DOWNLOADS_DIRECTORY_LABEL');
+    }
+    if (util.isPluginVmEnabled() && entry.fullPath == '/PluginVm') {
+      return str('PLUGIN_VM_DIRECTORY_LABEL');
+    }
   }
 
   return entry.name;
@@ -1510,6 +1514,12 @@
       loadTimeData.getBoolean('MY_FILES_VOLUME_ENABLED');
 };
 
+/** @return {boolean} */
+util.isPluginVmEnabled = () => {
+  return loadTimeData.valueExists('PLUGIN_VM_ENABLED') &&
+      loadTimeData.getBoolean('PLUGIN_VM_ENABLED');
+};
+
 /**
  * Used for logs and debugging. It tries to tell what type is the entry, its
  * path and URL.
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index a2a3b76..66d646b3 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -529,3 +529,11 @@
       url(../images/volumes/android_active.png) 1x,
       url(../images/volumes/2x/android_active.png) 2x);
 }
+
+[file-type-icon='plugin_vm'] {
+  background-image: url(../images/volumes/plugin_vm.svg);
+}
+
+.tree-row[selected] [file-type-icon='plugin_vm'] {
+  background-image: url(../images/volumes/plugin_vm_active.svg);
+}
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg
new file mode 100644
index 0000000..7cdd3608
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(90,90,90)">
+  <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/>
+</svg>
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg
new file mode 100644
index 0000000..66d714c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/plugin_vm_active.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(51,103,214)">
+  <path d="M7 12v4H4V4a1 1 0 0 1 1-1h8a3 3 0 0 1 3 3v3a3 3 0 0 1-3 3H7zm0-6v3h5a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H7z"/>
+</svg>
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 900ec81..8682728 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -272,12 +272,13 @@
 };
 
 /**
- * If entry is MyFiles/Downloads, we don't allow cut/delete/rename.
+ * If entry is MyFiles/Downloads or MyFiles/PluginVm, we don't allow
+ * cut/delete/rename.
  * @param {!VolumeManager} volumeManager
  * @param {(Entry|FakeEntry)} entry Entry or a fake entry.
  * @return {boolean}
  */
-CommandUtil.isDownloads = (volumeManager, entry) => {
+CommandUtil.isReadOnly = (volumeManager, entry) => {
   if (!entry) {
     return false;
   }
@@ -295,10 +296,13 @@
     return false;
   }
 
-  if (util.isMyFilesVolumeEnabled() &&
-      volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS &&
-      entry.fullPath === '/Downloads') {
-    return true;
+  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
+    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
+      return true;
+    }
+    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
+      return true;
+    }
   }
   return false;
 };
@@ -1031,7 +1035,7 @@
 
     /**
      * Returns True if any entry belongs to a read-only volume or is
-     * MyFiles>Downloads.
+     * forced to be read-only like MyFiles>Downloads.
      * @param {!Array<!Entry>} entries
      * @param {!CommandHandlerDeps} fileManager
      * @return {boolean} True if entries contain read only entry.
@@ -1040,7 +1044,7 @@
       return entries.some(entry => {
         const locationInfo = fileManager.volumeManager.getLocationInfo(entry);
         return (locationInfo && locationInfo.isReadOnly) ||
-            CommandUtil.isDownloads(fileManager.volumeManager, entry);
+            CommandUtil.isReadOnly(fileManager.volumeManager, entry);
       });
     }
   };
@@ -1195,7 +1199,7 @@
    */
   execute: function(event, fileManager) {
     const entry = CommandUtil.getCommandEntry(fileManager, event.target);
-    if (CommandUtil.isDownloads(fileManager.volumeManager, entry)) {
+    if (CommandUtil.isReadOnly(fileManager.volumeManager, entry)) {
       return;
     }
     if (event.target instanceof DirectoryTree ||
@@ -1270,7 +1274,7 @@
         !CommandUtil.shouldShowMenuItemsForEntry(
             fileManager.volumeManager, entries[0]) ||
         entries.some(
-            CommandUtil.isDownloads.bind(null, fileManager.volumeManager))) {
+            CommandUtil.isReadOnly.bind(null, fileManager.volumeManager))) {
       event.canExecute = false;
       event.command.setHidden(true);
       return;
@@ -2309,8 +2313,9 @@
     });
   },
   canExecute: function(event, fileManager) {
-    event.canExecute = fileManager.dialogType === DialogType.FULL_PAGE;
-    event.command.setHidden(!event.canExecute);
+    const isFullPage = fileManager.dialogType === DialogType.FULL_PAGE;
+    event.canExecute = isFullPage && navigator.onLine;
+    event.command.setHidden(!isFullPage);
   }
 });
 
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 51d9afe..8ab46bf4 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -1430,7 +1430,7 @@
     }
 
     // For MyFiles/Downloads we only allow copy.
-    if (isMove && this.isDownloads_(entry)) {
+    if (isMove && this.isCopyOnly_(entry)) {
       return false;
     }
 
@@ -1464,7 +1464,7 @@
 
   // For MyFiles/Downloads we only allow copy.
   if (isMove &&
-      this.selectionHandler_.selection.entries.some(this.isDownloads_, this)) {
+      this.selectionHandler_.selection.entries.some(this.isCopyOnly_, this)) {
     return false;
   }
 
@@ -1831,11 +1831,12 @@
 };
 
 /**
- * Returns True if entry is MyFiles>Downloads.
+ * Returns True if entry is folder which we enforce to be read-only
+ * or copy-only such as MyFiles>Downloads or MyFiles>PluginVm.
  * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry.
  * @return {boolean}
  */
-FileTransferController.prototype.isDownloads_ = function(entry) {
+FileTransferController.prototype.isCopyOnly_ = function(entry) {
   if (util.isFakeEntry(entry)) {
     return false;
   }
@@ -1845,10 +1846,13 @@
     return false;
   }
 
-  if (util.isMyFilesVolumeEnabled() &&
-      volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS &&
-      entry.fullPath === '/Downloads') {
-    return true;
+  if (volumeInfo.volumeType === VolumeManagerCommon.RootType.DOWNLOADS) {
+    if (util.isMyFilesVolumeEnabled() && entry.fullPath === '/Downloads') {
+      return true;
+    }
+    if (util.isPluginVmEnabled() && entry.fullPath === '/PluginVm') {
+      return true;
+    }
   }
   return false;
 };
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn
index d65a38d..16cf61f 100644
--- a/ui/file_manager/file_manager/test/BUILD.gn
+++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -43,6 +43,7 @@
     ":crostini_share",
     ":crostini_tasks",
     ":menu",
+    ":plugin_vm",
     ":progress_center",
     ":uma",
   ]
@@ -102,6 +103,13 @@
   ]
 }
 
+js_library("plugin_vm") {
+  deps = [
+    "js:test_util",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
 js_library("progress_center") {
   deps = [
     "js:test_util",
diff --git a/ui/file_manager/file_manager/test/check_select.js b/ui/file_manager/file_manager/test/check_select.js
index 3fb682dc..de358342 100644
--- a/ui/file_manager/file_manager/test/check_select.js
+++ b/ui/file_manager/file_manager/test/check_select.js
@@ -4,100 +4,80 @@
 
 const checkselect = {};
 
-checkselect.testCancelCheckSelectModeAfterAction = (done) => {
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Click 2nd last file on checkmark to start check-select-mode.
-        assertTrue(test.fakeMouseClick(
-            '#file-list li.table-row:nth-of-type(4) .detail-checkmark'));
-        return test.waitForElement(
-            '#file-list li[selected].table-row:nth-of-type(4)');
-      })
-      .then(result => {
-        // Click last file on checkmark, adds to selection.
-        assertTrue(test.fakeMouseClick(
-            '#file-list li.table-row:nth-of-type(5) .detail-checkmark'));
-        return test.waitForElement(
-            '#file-list li[selected].table-row:nth-of-type(5)');
-      })
-      .then(result => {
-        assertEquals(
-            2, document.querySelectorAll('#file-list li[selected]').length);
-        // Click selection menu (3-dots).
-        assertTrue(test.fakeMouseClick('#selection-menu-button'));
-        return test.waitForElement(
-            '#file-context-menu:not([hidden]) ' +
-            'cr-menu-item[command="#cut"]:not([disabled])');
-      })
-      .then(result => {
-        // Click 'cut'.
-        test.fakeMouseClick('#file-context-menu cr-menu-item[command="#cut"]');
-        return test.waitForElement('#file-context-menu[hidden]');
-      })
-      .then(result => {
-        // Click first photos dir in checkmark and make sure 4 and 5 not
-        // selected.
-        assertTrue(test.fakeMouseClick(
-            '#file-list li.table-row:nth-of-type(1) .detail-checkmark'));
-        return test.waitForElement(
-            '#file-list li[selected].table-row:nth-of-type(1)');
-      })
-      .then(result => {
-        assertEquals(
-            1, document.querySelectorAll('#file-list li[selected]').length);
-        done();
-      });
+checkselect.testCancelCheckSelectModeAfterAction = async (done) => {
+  await test.setupAndWaitUntilReady();
+
+  // Click 2nd last file on checkmark to start check-select-mode.
+  assertTrue(test.fakeMouseClick(
+      '#file-list li.table-row:nth-of-type(4) .detail-checkmark'));
+  await test.waitForElement('#file-list li[selected].table-row:nth-of-type(4)');
+
+  // Click last file on checkmark, adds to selection.
+  assertTrue(test.fakeMouseClick(
+      '#file-list li.table-row:nth-of-type(5) .detail-checkmark'));
+  await test.waitForElement('#file-list li[selected].table-row:nth-of-type(5)');
+  assertEquals(2, document.querySelectorAll('#file-list li[selected]').length);
+
+  // Click selection menu (3-dots).
+  assertTrue(test.fakeMouseClick('#selection-menu-button'));
+  await test.waitForElement(
+      '#file-context-menu:not([hidden]) ' +
+      'cr-menu-item[command="#cut"]:not([disabled])');
+
+  // Click 'cut'.
+  test.fakeMouseClick('#file-context-menu cr-menu-item[command="#cut"]');
+  await test.waitForElement('#file-context-menu[hidden]');
+
+  // Click first photos dir in checkmark and make sure 4 and 5 not
+  // selected.
+  assertTrue(test.fakeMouseClick(
+      '#file-list li.table-row:nth-of-type(1) .detail-checkmark'));
+  await test.waitForElement('#file-list li[selected].table-row:nth-of-type(1)');
+
+  assertEquals(1, document.querySelectorAll('#file-list li[selected]').length);
+  done();
 };
 
-checkselect.testCheckSelectModeAfterSelectAllOneFile = (done) => {
+checkselect.testCheckSelectModeAfterSelectAllOneFile = async (done) => {
   const gearMenu = document.querySelector('#gear-menu');
   const cancel = document.querySelector('#cancel-selection-button-wrapper');
   const selectAll =
       '#gear-menu:not([hidden]) #gear-menu-select-all:not([disabled])';
 
   // Load a single file.
-  test.setupAndWaitUntilReady([test.ENTRIES.hello])
-      .then(() => {
-        // Click gear menu, ensure 'Select all' is shown.
-        assertTrue(test.fakeMouseClick('#gear-button'));
-        return test.waitForElement(selectAll);
-      })
-      .then(result => {
-        // Click 'Select all', gear menu now replaced with file context menu.
-        assertTrue(test.fakeMouseClick('#gear-menu-select-all'));
-        return test.repeatUntil(() => {
-          return getComputedStyle(gearMenu).opacity == 0 &&
-              getComputedStyle(cancel).display == 'block' ||
-              test.pending('waiting for check select mode from click');
-        });
-      })
-      .then(result => {
-        // Cancel selection, ensure no items selected.
-        assertTrue(test.fakeMouseClick('#cancel-selection-button'));
-        return test.repeatUntil(() => {
-          return document.querySelectorAll('#file-list li[selected]').length ==
-              0 ||
-              test.pending('waiting for no files selected after click');
-        });
-      })
-      .then(result => {
-        // 'Ctrl+a' to select all.
-        assertTrue(test.fakeKeyDown('#file-list', 'a', true, false, false));
-        return test.repeatUntil(() => {
-          return getComputedStyle(cancel).display == 'block' ||
-              test.pending('waiting for check select mode from key');
-        });
-      })
-      .then(result => {
-        // Cancel selection, ensure no items selected.
-        assertTrue(test.fakeMouseClick('#cancel-selection-button'));
-        return test.repeatUntil(() => {
-          return document.querySelectorAll('#file-list li[selected]').length ==
-              0 ||
-              test.pending('waiting for no files selected after key');
-        });
-      })
-      .then(result => {
-        done();
-      });
+  await test.setupAndWaitUntilReady([test.ENTRIES.hello]);
+  // Click gear menu, ensure 'Select all' is shown.
+  assertTrue(test.fakeMouseClick('#gear-button'));
+  await test.waitForElement(selectAll);
+
+  // Click 'Select all', gear menu now replaced with file context menu.
+  assertTrue(test.fakeMouseClick('#gear-menu-select-all'));
+  await test.repeatUntil(() => {
+    return getComputedStyle(gearMenu).opacity == 0 &&
+        getComputedStyle(cancel).display == 'block' ||
+        test.pending('waiting for check select mode from click');
+  });
+
+  // Cancel selection, ensure no items selected.
+  assertTrue(test.fakeMouseClick('#cancel-selection-button'));
+  await test.repeatUntil(() => {
+    return document.querySelectorAll('#file-list li[selected]').length == 0 ||
+        test.pending('waiting for no files selected after click');
+  });
+
+  // 'Ctrl+a' to select all.
+  assertTrue(test.fakeKeyDown('#file-list', 'a', true, false, false));
+  await test.repeatUntil(() => {
+    return getComputedStyle(cancel).display == 'block' ||
+        test.pending('waiting for check select mode from key');
+  });
+
+  // Cancel selection, ensure no items selected.
+  assertTrue(test.fakeMouseClick('#cancel-selection-button'));
+  await test.repeatUntil(() => {
+    return document.querySelectorAll('#file-list li[selected]').length == 0 ||
+        test.pending('waiting for no files selected after key');
+  });
+
+  done();
 };
diff --git a/ui/file_manager/file_manager/test/crostini_mount.js b/ui/file_manager/file_manager/test/crostini_mount.js
index 60ee027..5ea485e 100644
--- a/ui/file_manager/file_manager/test/crostini_mount.js
+++ b/ui/file_manager/file_manager/test/crostini_mount.js
@@ -18,60 +18,53 @@
   done();
 };
 
-crostiniMount.testMountCrostiniSuccess = (done) => {
+crostiniMount.testMountCrostiniSuccess = async (done) => {
   const fakeRoot = '#directory-tree [root-type-icon="crostini"]';
   const oldMount = chrome.fileManagerPrivate.mountCrostini;
   let mountCallback = null;
   chrome.fileManagerPrivate.mountCrostini = (callback) => {
     mountCallback = callback;
   };
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Linux files fake root is shown.
-        return test.waitForElement(fakeRoot);
-      })
-      .then(() => {
-        // Click on Linux files.
-        assertTrue(test.fakeMouseClick(fakeRoot, 'click linux files'));
-        return test.waitForElement('paper-progress:not([hidden])');
-      })
-      .then(() => {
-        // Ensure mountCrostini is called.
-        return test.repeatUntil(() => {
-          if (!mountCallback) {
-            return test.pending('Waiting for mountCrostini');
-          }
-          return mountCallback;
-        });
-      })
-      .then(() => {
-        // Intercept the fileManagerPrivate.mountCrostini call
-        // and add crostini disk mount.
-        test.mountCrostini();
-        // Continue from fileManagerPrivate.mountCrostini callback
-        // and ensure expected files are shown.
-        mountCallback();
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
-      })
-      .then(() => {
-        // Reset fileManagerPrivate.mountCrostini and remove mount.
-        chrome.fileManagerPrivate.mountCrostini = oldMount;
-        chrome.fileManagerPrivate.removeMount('crostini');
-        // Linux Files fake root is shown.
-        return test.waitForElement(fakeRoot);
-      })
-      .then(() => {
-        // Downloads folder should be shown when crostini goes away.
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET));
-      })
-      .then(() => {
-        done();
-      });
+  await test.setupAndWaitUntilReady();
+
+  // Linux files fake root is shown.
+  await test.waitForElement(fakeRoot);
+
+  // Click on Linux files.
+  assertTrue(test.fakeMouseClick(fakeRoot, 'click linux files'));
+  await test.waitForElement('paper-progress:not([hidden])');
+
+  // Ensure mountCrostini is called.
+  await test.repeatUntil(() => {
+    if (!mountCallback) {
+      return test.pending('Waiting for mountCrostini');
+    }
+    return mountCallback;
+  });
+
+  // Intercept the fileManagerPrivate.mountCrostini call
+  // and add crostini disk mount.
+  test.mountCrostini();
+  // Continue from fileManagerPrivate.mountCrostini callback
+  // and ensure expected files are shown.
+  mountCallback();
+  await test.waitForFiles(
+      test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
+
+  // Reset fileManagerPrivate.mountCrostini and remove mount.
+  chrome.fileManagerPrivate.mountCrostini = oldMount;
+  chrome.fileManagerPrivate.removeMount('crostini');
+  // Linux Files fake root is shown.
+  await test.waitForElement(fakeRoot);
+
+  // MyFiles folder should be shown when crostini goes away.
+  await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
+      test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES));
+
+  done();
 };
 
-crostiniMount.testMountCrostiniError = (done) => {
+crostiniMount.testMountCrostiniError = async (done) => {
   const fakeRoot = '#directory-tree [root-type-icon="crostini"]';
   const oldMount = chrome.fileManagerPrivate.mountCrostini;
   // Override fileManagerPrivate.mountCrostini to return error.
@@ -80,47 +73,32 @@
     callback();
     delete chrome.runtime.lastError;
   };
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        return test.waitForElement(fakeRoot);
-      })
-      .then(() => {
-        // Click on Linux Files, ensure error dialog is shown.
-        assertTrue(test.fakeMouseClick(fakeRoot));
-        return test.waitForElement('.cr-dialog-container.shown');
-      })
-      .then(() => {
-        // Click OK button to close.
-        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container');
-      })
-      .then(() => {
-        // Reset chrome.fileManagerPrivate.mountCrostini.
-        chrome.fileManagerPrivate.mountCrostini = oldMount;
-        done();
-      });
+  await test.setupAndWaitUntilReady();
+  await test.waitForElement(fakeRoot);
+
+  // Click on Linux Files, ensure error dialog is shown.
+  assertTrue(test.fakeMouseClick(fakeRoot));
+  await test.waitForElement('.cr-dialog-container.shown');
+
+  // Click OK button to close.
+  assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
+  await test.waitForElementLost('.cr-dialog-container');
+
+  // Reset chrome.fileManagerPrivate.mountCrostini.
+  chrome.fileManagerPrivate.mountCrostini = oldMount;
+  done();
 };
 
-crostiniMount.testCrostiniMountOnDrag = (done) => {
+crostiniMount.testCrostiniMountOnDrag = async (done) => {
   const fakeRoot = '#directory-tree [root-type-icon="crostini"]';
   chrome.fileManagerPrivate.mountCrostiniDelay_ = 0;
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        return test.waitForElement(fakeRoot);
-      })
-      .then(() => {
-        assertTrue(
-            test.sendEvent(fakeRoot, new Event('dragenter', {bubbles: true})));
-        assertTrue(
-            test.sendEvent(fakeRoot, new Event('dragleave', {bubbles: true})));
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
-      })
-      .then(() => {
-        chrome.fileManagerPrivate.removeMount('crostini');
-        return test.waitForElement(fakeRoot);
-      })
-      .then(() => {
-        done();
-      });
+  await test.setupAndWaitUntilReady();
+  await test.waitForElement(fakeRoot);
+  assertTrue(test.sendEvent(fakeRoot, new Event('dragenter', {bubbles: true})));
+  assertTrue(test.sendEvent(fakeRoot, new Event('dragleave', {bubbles: true})));
+  await test.waitForFiles(
+      test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
+  chrome.fileManagerPrivate.removeMount('crostini');
+  await test.waitForElement(fakeRoot);
+  done();
 };
diff --git a/ui/file_manager/file_manager/test/crostini_share.js b/ui/file_manager/file_manager/test/crostini_share.js
index 688fb1d..3db134b 100644
--- a/ui/file_manager/file_manager/test/crostini_share.js
+++ b/ui/file_manager/file_manager/test/crostini_share.js
@@ -36,7 +36,7 @@
   const shareWithDirTree =
       '#directory-tree-context-menu [command="#' + share + '"]';
   const photos = '#file-list [file-name="photos"]';
-  const downloadsDirTree = '#directory-tree [volume-type-icon="downloads"]';
+  const myFilesDirTree = '#directory-tree [root-type-icon="my_files"]';
   const oldSharePaths = chrome.fileManagerPrivate.sharePathsWithCrostini;
   let sharePathsCalled = false;
   let sharePathsPersist;
@@ -116,10 +116,9 @@
   assertTrue(test.fakeMouseRightClick(photos), 'right-click photos');
   await test.waitForElement(menuShareWith);
 
-  // Verify dialog is shown for Downloads root.
+  // Verify dialog is shown for MyFiles root.
   // Check 'Share with <VM>' is shown in menu.
-  assertTrue(
-      test.fakeMouseRightClick(downloadsDirTree), 'right-click downloads');
+  assertTrue(test.fakeMouseRightClick(myFilesDirTree), 'right-click MyFiles');
   await test.waitForElement(menuShareWithDirTree);
 
   // Click 'Share with <VM>', verify dialog.
@@ -167,7 +166,6 @@
       '[command="#' + share + '"][hidden][disabled="disabled"]';
   const menuShareWith = '#file-context-menu:not([hidden]) ' +
       '[command="#' + share + '"]:not([hidden]):not([disabled])';
-  const downloadsDirTree = '#directory-tree [volume-type-icon="downloads"]';
   const removableVolumeRoot = '#directory-tree [volume-type-icon="removable"]';
   const menuShareWithDirTree = '#directory-tree-context-menu:not([hidden]) ' +
       '[command="#' + share + '"]:not([hidden]):not([disabled])';
@@ -203,9 +201,9 @@
   assertTrue(test.fakeMouseRightClick(downloads), 'right-click downloads');
   await test.waitForElement(menuShareWith);
 
-  // Right-click 'Downloads' directory in directory tree.
+  // Right-click 'MyFiles' in directory tree.
   // Check 'Share with <VM>' is shown in menu.
-  assertTrue(test.fakeMouseRightClick(downloadsDirTree), 'downloads dirtree');
+  assertTrue(test.fakeMouseRightClick(myFiles), 'MyFiles dirtree');
   await test.waitForElement(menuShareWithDirTree);
 
   // Select removable root.
diff --git a/ui/file_manager/file_manager/test/crostini_tasks.js b/ui/file_manager/file_manager/test/crostini_tasks.js
index 2dab4d18..a2f5bef 100644
--- a/ui/file_manager/file_manager/test/crostini_tasks.js
+++ b/ui/file_manager/file_manager/test/crostini_tasks.js
@@ -4,7 +4,9 @@
 
 const crostiniTasks = {};
 
-crostiniTasks.testShareBeforeOpeningDownloadsWithCrostiniApp = (done) => {
+crostiniTasks.testShareBeforeOpeningDownloadsWithCrostiniApp = async (done) => {
+  const fakeRoot = '#directory-tree [root-type-icon="crostini"]';
+
   // Save old fmp.getFileTasks and replace with version that returns
   // crostini app and chrome Text app.
   let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks;
@@ -44,70 +46,62 @@
   };
   chrome.metricsPrivate.values_ = [];
 
-  test.setupAndWaitUntilReady([], [], [])
-      .then(() => {
-        // Add '/A', and '/A/hello.txt', refresh, 'A' is shown.
-        test.addEntries(
-            [test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []);
-        assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows([test.ENTRIES.directoryA]));
-      })
-      .then(() => {
-        // Change to 'A' directory, hello.txt is shown.
-        assertTrue(test.fakeMouseDoubleClick('[file-name="A"]'));
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello]));
-      })
-      .then(() => {
-        // Right click on 'hello.txt' file, wait for dialog with 'Open with'.
-        assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]'));
-        return test.waitForElement(
-            'cr-menu-item[command="#open-with"]:not([hidden]');
-      })
-      .then(() => {
-        // Click 'Open with', wait for picker.
-        assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]'));
-        return test.waitForElement('#default-tasks-list');
-      })
-      .then(() => {
-        // Ensure that the default tasks label is shown correctly.
-        const item = document.querySelector('#default-task-menu-item span');
-        assertEquals('Open with Crostini App', item.innerText);
-      })
-      .then(() => {
-        // Ensure picker shows both options.  Click on 'Crostini App'.  Ensure
-        // share path dialog is shown.
-        const list = document.querySelectorAll('#default-tasks-list li div');
-        assertEquals(2, list.length);
-        assertEquals('Crostini App (default)', list[0].innerText);
-        assertEquals('Open with Text', list[1].innerText);
-        assertTrue(test.fakeMouseClick('#default-tasks-list li'));
-        // Ensure fmp.sharePathsWithCrostini, fmp.executeTask called.
-        return test.repeatUntil(() => {
-          return sharePathsCalled && executeTaskCalled ||
-              test.pending('Waiting to share and open');
-        });
-      })
-      .then(() => {
-        // Share should not persist as a result of open with crostini app.
-        assertFalse(sharePathsPersist);
-        // Validate UMAs.
-        const lastEnumUma = chrome.metricsPrivate.values_.pop();
-        assertEquals(
-            'FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName);
-        assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]);
+  await test.setupAndWaitUntilReady([], [], []);
 
-        // Restore fmp.*.
-        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
-        chrome.fileManagerPrivate.sharePathsWithCrostini = oldSharePaths;
-        chrome.fileManagerPrivate.executeTask = oldExecuteTask;
-        done();
-      });
+  // Add '/A', and '/A/hello.txt', refresh, 'A' is shown.
+  test.addEntries([test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []);
+  assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
+  await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
+      [test.ENTRIES.directoryA, test.ENTRIES.linuxFiles]));
+
+  // Change to 'A' directory, hello.txt is shown.
+  assertTrue(test.fakeMouseDoubleClick('[file-name="A"]'));
+  await test.waitForFiles(
+      test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello]));
+
+  // Right click on 'hello.txt' file, wait for dialog with 'Open with'.
+  assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]'));
+  await test.waitForElement('cr-menu-item[command="#open-with"]:not([hidden]');
+
+  // Click 'Open with', wait for picker.
+  assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]'));
+  await test.waitForElement('#default-tasks-list');
+
+  // Ensure that the default tasks label is shown correctly.
+  const item = document.querySelector('#default-task-menu-item span');
+  assertEquals('Open with Crostini App', item.innerText);
+
+  // Ensure picker shows both options.  Click on 'Crostini App'.  Ensure
+  // share path dialog is shown.
+  const list = document.querySelectorAll('#default-tasks-list li div');
+  assertEquals(2, list.length);
+  assertEquals('Crostini App (default)', list[0].innerText);
+  assertEquals('Open with Text', list[1].innerText);
+  assertTrue(test.fakeMouseClick('#default-tasks-list li'));
+  // Ensure fmp.sharePathsWithCrostini, fmp.executeTask called.
+  await test.repeatUntil(() => {
+    return sharePathsCalled && executeTaskCalled ||
+        test.pending('Waiting to share and open');
+  });
+
+  // Share should not persist as a result of open with crostini app.
+  assertFalse(sharePathsPersist);
+  // Validate UMAs.
+  const lastEnumUma = chrome.metricsPrivate.values_.pop();
+  assertEquals('FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName);
+  assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]);
+
+  // Restore fmp.*.
+  chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
+  chrome.fileManagerPrivate.sharePathsWithCrostini = oldSharePaths;
+  chrome.fileManagerPrivate.executeTask = oldExecuteTask;
+  chrome.fileManagerPrivate.removeMount('crostini');
+  await test.waitForElement(fakeRoot);
+  done();
 };
 
-crostiniTasks.testErrorLoadingLinuxPackageInfo = (done) => {
-  const linuxFiles = '#directory-tree .tree-item [root-type-icon="crostini"]';
+crostiniTasks.testErrorLoadingLinuxPackageInfo = async (done) => {
+  const fakeRoot = '#directory-tree [root-type-icon="crostini"]';
   const dialog = '#install-linux-package-dialog';
   const detailsFrame = '.install-linux-package-details-frame';
 
@@ -132,48 +126,43 @@
     packageInfoCallback = callback;
   };
 
-  test.setupAndWaitUntilReady([], [], [test.ENTRIES.debPackage])
-      .then(() => {
-        return test.waitForElement(linuxFiles);
-      })
-      .then(() => {
-        // Select 'Linux files' in directory tree.
-        assertTrue(test.fakeMouseClick(linuxFiles), 'click Linux files');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows([test.ENTRIES.debPackage]));
-      })
-      .then(() => {
-        // Double click on 'package.deb' file to open the install dialog.
-        assertTrue(test.fakeMouseDoubleClick('[file-name="package.deb"]'));
-        return test.waitForElement(dialog);
-      })
-      .then(() => {
-        // Verify the loading state is shown.
-        assertEquals(
-            'Details\nLoading information...',
-            document.querySelector(detailsFrame).innerText);
-        return test.repeatUntil(() => {
-          return packageInfoCallback ||
-              test.pending('Waiting for package info request');
-        });
-      })
-      .then(() => {
-        // Call the callback with an error.
-        chrome.runtime.lastError = {message: 'error message'};
-        packageInfoCallback(undefined);
-        delete chrome.runtime.lastError;
-        assertEquals(
-            'Details\nFailed to retrieve app info.',
-            document.querySelector(detailsFrame).innerText);
+  await test.setupAndWaitUntilReady([], [], [test.ENTRIES.debPackage]);
+  await test.waitForElement(fakeRoot);
 
-        // Click 'cancel' to close.  Ensure dialog closes.
-        assertTrue(test.fakeMouseClick('button.cr-dialog-cancel'));
-        return test.waitForElementLost(dialog);
-      })
-      .then(() => {
-        // Restore fmp.getFileTasks, fmp.getLinuxPackageInfo.
-        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
-        chrome.fileManagerPrivate.getLinuxPackageInfo = oldGetLinuxPackageInfo;
-        done();
-      });
+  // Select 'Linux files' in directory tree.
+  assertTrue(test.fakeMouseClick(fakeRoot), 'click Linux files');
+  await test.waitForFiles(
+      test.TestEntryInfo.getExpectedRows([test.ENTRIES.debPackage]));
+
+  // Double click on 'package.deb' file to open the install dialog.
+  assertTrue(test.fakeMouseDoubleClick('[file-name="package.deb"]'));
+  await test.waitForElement(dialog);
+
+  // Verify the loading state is shown.
+  assertEquals(
+      'Details\nLoading information...',
+      document.querySelector(detailsFrame).innerText);
+  await test.repeatUntil(() => {
+    return packageInfoCallback ||
+        test.pending('Waiting for package info request');
+  });
+
+  // Call the callback with an error.
+  chrome.runtime.lastError = {message: 'error message'};
+  packageInfoCallback(undefined);
+  delete chrome.runtime.lastError;
+  assertEquals(
+      'Details\nFailed to retrieve app info.',
+      document.querySelector(detailsFrame).innerText);
+
+  // Click 'cancel' to close.  Ensure dialog closes.
+  assertTrue(test.fakeMouseClick('button.cr-dialog-cancel'));
+  await test.waitForElementLost(dialog);
+
+  // Restore fmp.getFileTasks, fmp.getLinuxPackageInfo.
+  chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
+  chrome.fileManagerPrivate.getLinuxPackageInfo = oldGetLinuxPackageInfo;
+  chrome.fileManagerPrivate.removeMount('crostini');
+  await test.waitForElement(fakeRoot);
+  done();
 };
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index 9d7fbcc..5d8a144 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -8,7 +8,7 @@
 loadTimeData.data = $GRDP;
 
 // Extend with additional fields not found in grdp files.
-Object.setPrototypeOf(loadTimeData.data_, {
+loadTimeData.overrideValues({
   'CROSTINI_ENABLED': true,
   'DRIVE_FS_ENABLED': false,
   'GOOGLE_DRIVE_REDEEM_URL': 'http://www.google.com/intl/en/chrome/devices' +
@@ -16,6 +16,7 @@
   'GOOGLE_DRIVE_OVERVIEW_URL':
       'https://support.google.com/chromebook/?p=filemanager_drive',
   'HIDE_SPACE_INFO': false,
+  'MY_FILES_VOLUME_ENABLED': true,
   'PLUGIN_VM_ENABLED': true,
   'UI_LOCALE': 'en_US',
   'language': 'en-US',
@@ -25,4 +26,4 @@
 // Overwrite LoadTimeData.prototype.data setter as nop.
 // Default implementation throws errors when both background and
 // foreground re-set loadTimeData.data.
-Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}});
+Object.defineProperty(LoadTimeData.prototype, 'data', {set: () => {}});
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/test/js/test_util.js b/ui/file_manager/file_manager/test/js/test_util.js
index 6e64ec3e..9328787 100644
--- a/ui/file_manager/file_manager/test/js/test_util.js
+++ b/ui/file_manager/file_manager/test/js/test_util.js
@@ -127,7 +127,7 @@
   var content = test.DATA[this.sourceFileName];
   var size = content && content.size || 0;
   return {
-    fullPath: prefix + this.nameText + suffix,
+    fullPath: prefix + this.targetPath + suffix,
     metadata: {
       size: size,
       modificationTime: new Date(Date.parse(this.lastModifiedTime)),
@@ -188,12 +188,12 @@
       'Jan 1, 1980, 11:59 PM', 'photos', '--', 'Folder'),
 
   testDocument: new test.TestEntryInfo(
-      test.EntryType.FILE, '', 'Test Document',
+      test.EntryType.FILE, '', 'Test Document.gdoc',
       'application/vnd.google-apps.document', test.SharedOption.NONE,
       'Apr 10, 2013, 4:20 PM', 'Test Document.gdoc', '--', 'Google document'),
 
   testSharedDocument: new test.TestEntryInfo(
-      test.EntryType.FILE, '', 'Test Shared Document',
+      test.EntryType.FILE, '', 'Test Shared Document.gdoc',
       'application/vnd.google-apps.document', test.SharedOption.SHARED,
       'Mar 20, 2013, 10:40 PM', 'Test Shared Document.gdoc', '--',
       'Google document'),
@@ -207,26 +207,6 @@
       test.EntryType.DIRECTORY, '', 'A', '', test.SharedOption.NONE,
       'Jan 1, 2000, 1:00 AM', 'A', '--', 'Folder'),
 
-  directoryB: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'A/B', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'B', '--', 'Folder'),
-
-  directoryC: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'A/B/C', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'C', '--', 'Folder'),
-
-  directoryD: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'D', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'D', '--', 'Folder'),
-
-  directoryE: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'D/E', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'E', '--', 'Folder'),
-
-  directoryF: new test.TestEntryInfo(
-      test.EntryType.DIRECTORY, '', 'D/E/F', '', test.SharedOption.NONE,
-      'Jan 1, 2000, 1:00 AM', 'F', '--', 'Folder'),
-
   zipArchive: new test.TestEntryInfo(
       test.EntryType.FILE, 'archive.zip', 'archive.zip', 'application/x-zip',
       test.SharedOption.NONE, 'Jan 1, 2014, 1:00 AM', 'archive.zip',
@@ -243,25 +223,51 @@
       '51 bytes', 'Plain text'),
 
   helloInA: new test.TestEntryInfo(
-      test.EntryType.FILE, 'text.txt', 'hello.txt', 'text/plain',
-      test.SharedOption.NONE, 'Sep 4, 1998, 12:34 PM', 'A/hello.txt',
-      '51 bytes', 'Plain text'),
+      test.EntryType.FILE, 'text.txt', 'A/hello.txt', 'text/plain',
+      test.SharedOption.NONE, 'Sep 4, 1998, 12:34 PM', 'hello.txt', '51 bytes',
+      'Plain text'),
+
+  downloads: new test.TestEntryInfo(
+      test.EntryType.DIRECTORY, '', 'Downloads', '', test.SharedOption.NONE,
+      'Jan 1, 2000, 1:00 AM', 'Downloads', '--', 'Folder'),
+
+  linuxFiles: new test.TestEntryInfo(
+      test.EntryType.DIRECTORY, '', 'Linux files', '', test.SharedOption.NONE,
+      '...', 'Linux files', '--', 'Folder'),
+
+  pluginVm: new test.TestEntryInfo(
+      test.EntryType.DIRECTORY, '', 'PluginVm', '', test.SharedOption.NONE,
+      'Jan 1, 2000, 1:00 AM', 'Plugin VM', '--', 'Folder'),
+
+  photosInPluginVm: new test.TestEntryInfo(
+      test.EntryType.DIRECTORY, '', 'PluginVm/photos', '',
+      test.SharedOption.NONE, 'Jan 1, 1980, 11:59 PM', 'photos', '--',
+      'Folder'),
 };
 
 /**
- * Basic entry set for the local volume.
+ * Basic entry set for the MyFiles volume.
  * @type {!Array<!test.TestEntryInfo>}
  * @const
  */
-test.BASIC_LOCAL_ENTRY_SET = [
+test.BASIC_MY_FILES_ENTRY_SET = [
+  test.ENTRIES.downloads,
   test.ENTRIES.hello,
   test.ENTRIES.world,
   test.ENTRIES.desktop,
   test.ENTRIES.beautiful,
-  test.ENTRIES.photos
+  test.ENTRIES.photos,
 ];
 
 /**
+ * MyFiles plus the fake item 'Linux files'.
+ * @type {!Array<!test.TestEntryInfo>}
+ * @const
+ */
+test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES =
+    test.BASIC_MY_FILES_ENTRY_SET.concat([test.ENTRIES.linuxFiles]);
+
+/**
  * Basic entry set for the drive volume.
  *
  * TODO(hirono): Add a case for an entry cached by FileCache. For testing
@@ -532,14 +538,15 @@
  * Opens a Files app's main window and waits until it is initialized. Fills
  * the window with initial files. Should be called for the first window only.
  *
- * @param {Array<!test.TestEntryInfo>=} opt_downloads Entries for downloads.
+ * @param {Array<!test.TestEntryInfo>=} opt_myFiles Entries for MyFiles.
  * @param {Array<!test.TestEntryInfo>=} opt_drive Entries for drive.
  * @param {Array<!test.TestEntryInfo>=} opt_crostini Entries for crostini.
  * @return {Promise} Promise to be fulfilled with the result object, which
  *     contains the file list.
  */
-test.setupAndWaitUntilReady = function(opt_downloads, opt_drive, opt_crostini) {
-  const entriesDownloads = opt_downloads || test.BASIC_LOCAL_ENTRY_SET;
+test.setupAndWaitUntilReady =
+    async function(opt_myFiles, opt_drive, opt_crostini) {
+  const entriesMyFiles = opt_myFiles || test.BASIC_MY_FILES_ENTRY_SET;
   const entriesDrive = opt_drive || test.BASIC_DRIVE_ENTRY_SET;
   const entriesCrostini = opt_crostini || test.BASIC_CROSTINI_ENTRY_SET;
 
@@ -555,22 +562,19 @@
   test.inputText = test.util.sync.inputText.bind(null, window);
   test.selectFile = test.util.sync.selectFile.bind(null, window);
 
-  const downloadsElement = '#directory-tree [volume-type-icon="downloads"]';
+  const myFilesElement = '#directory-tree [root-type-icon="my_files"]';
 
-  return test.loadData()
-      .then(() => {
-        test.addEntries(entriesDownloads, entriesDrive, entriesCrostini);
-        return test.waitForElement(downloadsElement);
-      })
-      .then((downloadsIcon) => {
-        // Click Downloads if not already on Downloads, then refresh button.
-        if (!downloadsIcon.parentElement.hasAttribute('selected')) {
-          assertTrue(test.fakeMouseClick(downloadsElement), 'click downloads');
-        }
-        assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(entriesDownloads));
-      });
+  await test.loadData();
+  test.addEntries(entriesMyFiles, entriesDrive, entriesCrostini);
+  const myFiles = await test.waitForElement(myFilesElement);
+
+  // Click MyFiles if not already on MyFiles, then refresh button.
+  if (!myFiles.parentElement.hasAttribute('selected')) {
+    assertTrue(test.fakeMouseClick(myFilesElement), 'click MyFiles');
+  }
+  assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
+  const filesShown = entriesMyFiles.concat([test.ENTRIES.linuxFiles]);
+  return test.waitForFiles(test.TestEntryInfo.getExpectedRows(filesShown));
 };
 
 /**
@@ -580,29 +584,3 @@
 test.done = function(opt_failed) {
   window.endTests(!opt_failed);
 };
-
-/**
- * @return {number} Maximum listitem-? id from #file-list.
- */
-test.maxListItemId = function() {
-  var listItems = document.querySelectorAll('#file-list .table-row');
-  if (!listItems) {
-    return 0;
-  }
-  return Math.max(...Array.from(listItems).map(e => {
-    return e.id.replace('listitem-', '');
-  }));
-};
-
-/**
- * @return {number} Minium listitem-? id from #file-list.
- */
-test.minListItemId = function() {
-  var listItems = document.querySelectorAll('#file-list .table-row');
-  if (!listItems) {
-    return 0;
-  }
-  return Math.min(...Array.from(listItems).map(e => {
-    return e.id.replace('listitem-', '');
-  }));
-};
diff --git a/ui/file_manager/file_manager/test/plugin_vm.js b/ui/file_manager/file_manager/test/plugin_vm.js
new file mode 100644
index 0000000..05c978c
--- /dev/null
+++ b/ui/file_manager/file_manager/test/plugin_vm.js
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const pluginVm = {};
+
+pluginVm.testLabelIconContextMenu = async (done) => {
+  const fileMenu = [
+    ['#cut', false],
+    ['#copy', true],
+    ['#paste-into-folder', false],
+    ['#get-info', true],
+    ['#delete', false],
+    ['#zip-selection', true],
+    ['#share-with-linux', true],
+    ['#manage-plugin-vm-sharing', true],
+    ['#new-folder', true],
+  ];
+
+  const fileMenuSubfolder = [
+    ['#cut', true],
+    ['#copy', true],
+    ['#paste-into-folder', false],
+    ['#get-info', true],
+    ['#rename', true],
+    ['#delete', true],
+    ['#zip-selection', true],
+    ['#share-with-linux', true],
+    ['#manage-plugin-vm-sharing', true],
+    ['#new-folder', true],
+  ];
+
+  const pluginVmFolder = '#file-list [file-name="PluginVm"]';
+  const iconFolder =
+      '#file-list [file-name="PluginVm"] [file-type-icon="plugin_vm"]';
+  const fileMenuShown = '#file-context-menu:not([hidden])';
+
+  const iconDirTree = '#directory-tree [file-type-icon="plugin_vm"]';
+  const dirTreeMenuShown = '#directory-tree-context-menu:not([hidden])';
+  const itemsShown = ' cr-menu-item:not([hidden])';
+
+  async function menuItems(menuTypeShown) {
+    await test.waitForElement(menuTypeShown);
+    const list = document.querySelectorAll(menuTypeShown + itemsShown);
+    return Array.from(document.querySelectorAll(menuTypeShown + itemsShown))
+        .map(e => [e.attributes['command'].value, !e.disabled]);
+  }
+
+  // Verify that /PluginVm has label 'Plugin VM'.
+  await test.setupAndWaitUntilReady([], [], []);
+  test.addEntries(
+      [test.ENTRIES.pluginVm, test.ENTRIES.photosInPluginVm], [], []);
+  assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
+  await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
+      [test.ENTRIES.pluginVm, test.ENTRIES.linuxFiles]));
+
+  // Register /PluginVm as shared.
+  const pluginVmEntry =
+      mockVolumeManager
+          .getCurrentProfileVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS)
+          .fileSystem.entries['/PluginVm'];
+  fileManager.crostini.registerSharedPath('PluginVm', pluginVmEntry);
+
+  // Verify folder icon.
+  await test.waitForElement(iconFolder);
+
+  // Verify /PluginVm folder context menu.
+  assertTrue(test.fakeMouseRightClick(pluginVmFolder));
+  let items = await menuItems(fileMenuShown);
+  assertEquals(JSON.stringify(fileMenu), JSON.stringify(items));
+
+  // Change to 'PluginVm' directory, photos folder is shown.
+  assertTrue(test.fakeMouseDoubleClick(pluginVmFolder));
+  await test.waitForFiles(
+      test.TestEntryInfo.getExpectedRows([test.ENTRIES.photos]));
+
+  // Verify /PluginVm/photos folder context menu.
+  assertTrue(test.fakeMouseRightClick('#file-list [file-name="photos"]'));
+  items = await menuItems(fileMenuShown);
+  assertEquals(JSON.stringify(fileMenuSubfolder), JSON.stringify(items));
+
+  done();
+};
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/test/progress_center.js b/ui/file_manager/file_manager/test/progress_center.js
index db21f04..bd8248c1 100644
--- a/ui/file_manager/file_manager/test/progress_center.js
+++ b/ui/file_manager/file_manager/test/progress_center.js
@@ -16,7 +16,7 @@
   return item;
 };
 
-progressCenter.testScrollWhenManyMessages = (done) => {
+progressCenter.testScrollWhenManyMessages = async (done) => {
   const visibleClosed = '#progress-center:not([hidden]):not(.opened)';
   const visibleOpen = '#progress-center:not([hidden]).opened';
   const openIcon = '#progress-center-close-view .open';
@@ -25,32 +25,29 @@
   const items = [];
   const center = fileManager.fileBrowserBackground_.progressCenter;
   // Load a single file.
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Add lots of messages.
-        for (let i = 0; i < 100; i++) {
-          const item = progressCenter.createItem('id' + i, 'msg ' + i);
-          items.push(item);
-          center.updateItem(item);
-        }
-        // Wait for notification expand icon.
-        return test.waitForElement(visibleClosed);
-      })
-      .then(result => {
-        // Click open icon, ensure progress center is open.
-        assertTrue(test.fakeMouseClick(openIcon));
-        return test.waitForElement(visibleOpen);
-      })
-      .then(result => {
-        // Ensure progress center is scrollable.
-        const footer = document.querySelector(navListFooter);
-        assertTrue(footer.scrollHeight > footer.clientHeight);
+  await test.setupAndWaitUntilReady();
 
-        // Clear items.
-        items.forEach((item) => {
-          item.state = ProgressItemState.COMPLETED;
-          center.updateItem(item);
-        });
-        done();
-      });
+  // Add lots of messages.
+  for (let i = 0; i < 100; i++) {
+    const item = progressCenter.createItem('id' + i, 'msg ' + i);
+    items.push(item);
+    center.updateItem(item);
+  }
+  // Wait for notification expand icon.
+  await test.waitForElement(visibleClosed);
+
+  // Click open icon, ensure progress center is open.
+  assertTrue(test.fakeMouseClick(openIcon));
+  await test.waitForElement(visibleOpen);
+
+  // Ensure progress center is scrollable.
+  const footer = document.querySelector(navListFooter);
+  assertTrue(footer.scrollHeight > footer.clientHeight);
+
+  // Clear items.
+  items.forEach((item) => {
+    item.state = ProgressItemState.COMPLETED;
+    center.updateItem(item);
+  });
+  done();
 };
diff --git a/ui/file_manager/file_manager/test/uma.js b/ui/file_manager/file_manager/test/uma.js
index 11b172e..ad62fe9 100644
--- a/ui/file_manager/file_manager/test/uma.js
+++ b/ui/file_manager/file_manager/test/uma.js
@@ -4,27 +4,23 @@
 
 const uma = {};
 
-uma.testClickBreadcrumb = (done) => {
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Reset metrics.
-        chrome.metricsPrivate.userActions_ = [];
-        // Click first row which is 'photos' dir, wait for breadcrumb to show.
-        assertTrue(test.fakeMouseDoubleClick('#file-list li.table-row'));
-        return test.waitForElement(
-            '#location-breadcrumbs .breadcrumb-path:nth-of-type(2)');
-      })
-      .then(result => {
-        // Click breadcrumb to return to parent dir.
-        assertTrue(test.fakeMouseClick(
-            '#location-breadcrumbs .breadcrumb-path:nth-of-type(1)'));
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET));
-      })
-      .then(result => {
-        assertArrayEquals(
-            ['FileBrowser.ClickBreadcrumbs'],
-            chrome.metricsPrivate.userActions_);
-        done();
-      });
+uma.testClickBreadcrumb = async (done) => {
+  await test.setupAndWaitUntilReady();
+
+  // Reset metrics.
+  chrome.metricsPrivate.userActions_ = [];
+  // Click first row which is 'photos' dir, wait for breadcrumb to show.
+  assertTrue(test.fakeMouseDoubleClick('#file-list li.table-row'));
+  await test.waitForElement(
+      '#location-breadcrumbs .breadcrumb-path:nth-of-type(2)');
+
+  // Click breadcrumb to return to parent dir.
+  assertTrue(test.fakeMouseClick(
+      '#location-breadcrumbs .breadcrumb-path:nth-of-type(1)'));
+  await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
+      test.BASIC_MY_FILES_ENTRY_SET_WITH_LINUX_FILES));
+
+  assertArrayEquals(
+      ['FileBrowser.ClickBreadcrumbs'], chrome.metricsPrivate.userActions_);
+  done();
 };
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css
index 41d67ce9..2710c4e 100644
--- a/ui/file_manager/gallery/css/gallery.css
+++ b/ui/file_manager/gallery/css/gallery.css
@@ -173,6 +173,12 @@
 .gallery .video-container > .video {
   max-height: 100%;
   max-width: 100%;
+
+  /*
+   * Since r614513, <video> elements take focus on click which causes yellow
+   * lines to appear unless the outline is disabled. See crbug/917503.
+   */
+  outline: none;
 }
 
 /*
diff --git a/ui/file_manager/image_loader/piex/.gitignore b/ui/file_manager/image_loader/piex/.gitignore
index 53d1e6ef..2ae8f3f 100644
--- a/ui/file_manager/image_loader/piex/.gitignore
+++ b/ui/file_manager/image_loader/piex/.gitignore
@@ -3,5 +3,5 @@
 package-lock.json
 tests.result.txt
 tests.log
-a.out.*
+piex.out*
 *.bc
diff --git a/ui/file_manager/image_loader/piex/Makefile b/ui/file_manager/image_loader/piex/Makefile
index 7e264c0a..4c489bc 100644
--- a/ui/file_manager/image_loader/piex/Makefile
+++ b/ui/file_manager/image_loader/piex/Makefile
@@ -15,6 +15,7 @@
 WASM = -s WASM=1 -fno-exceptions -Wall
 WOPT = -Os --llvm-opts 3 \
   -s STRICT=1 \
+  -s SINGLE_FILE=1 \
   -s ALLOW_MEMORY_GROWTH=1 \
   -s ENVIRONMENT='web' \
   -s NO_DYNAMIC_EXECUTION=1 \
@@ -22,11 +23,12 @@
   -s RESERVED_FUNCTION_POINTERS=$(shell echo $$((10*2))) \
   -s TOTAL_STACK=$(shell echo $$((8*1024)))
 
-all: a.out.js
-	$(shell cp a.out.* extension)
+.PHONY: all clean release
 
-a.out.js: piex.cpp.bc piex.src.bc
-	em++ --bind --std=c++11 $(WASM) $(WOPT) $^
+all: piex.out.js release
+
+piex.out.js: piex.cpp.bc piex.src.bc
+	em++ --bind --std=c++11 $(WASM) $(WOPT) $^ -o $@
 
 piex.cpp.bc: piex.cpp Makefile
 	em++ --bind --std=c++11 $(WASM) $(WOPT) $(INCS) piex.cpp -o $@
@@ -34,8 +36,10 @@
 piex.src.bc: $(PSRC) Makefile
 	em++ --bind --std=c++11 $(WASM) $(WOPT) $(INCS) $(PSRC) -o $@
 
-.PHONY: clean
+release:
+	$(shell cp piex.out.js piex.wasm)
+	$(shell cp piex.wasm extension)
 
 clean:
-	$(shell rm -f tests.{result*,log} package-lock* *.bc a.out.*)
-	$(shell rm -f extension/a.out.*)
+	$(shell rm -f tests.{result*,log} package-lock* *.bc piex.out*)
+	$(shell rm -f piex.wasm extension/piex.wasm)
diff --git a/ui/file_manager/image_loader/piex/README.md b/ui/file_manager/image_loader/piex/README.md
index 256ee901..2b93f673 100644
--- a/ui/file_manager/image_loader/piex/README.md
+++ b/ui/file_manager/image_loader/piex/README.md
@@ -15,15 +15,21 @@
   npm install
 ```
 
-Build piexwasm code a.out.js a.out.wasm
+Build piexwasm code: piex.wasm
 
 ```shell
   npm run build
 ```
 
-Run tests
+Run tests: they must PASS
 
 ```shell
   npm run test
 ```
 
+Release: submit piex.wasm to the Chromium repository
+
+```shell
+  git commit -a -m "Release piexwasm ..."
+  git cl upload
+```
diff --git a/ui/file_manager/image_loader/piex/extension/background.js b/ui/file_manager/image_loader/piex/extension/background.js
index 70cab29..83eaafdc 100644
--- a/ui/file_manager/image_loader/piex/extension/background.js
+++ b/ui/file_manager/image_loader/piex/extension/background.js
@@ -20,5 +20,5 @@
     },
   };
 
-  script.src = 'a.out.js';
+  script.src = '/piex.wasm';
 };
diff --git a/ui/file_manager/image_loader/piex/piex.wasm b/ui/file_manager/image_loader/piex/piex.wasm
new file mode 100644
index 0000000..5be8714
--- /dev/null
+++ b/ui/file_manager/image_loader/piex/piex.wasm
@@ -0,0 +1 @@
+var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=function(title){document.title=title}}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var asm2wasmImports={"f64-rem":function(x,y){return x%y},"debugger":function(){debugger}};var jsCallStartIndex=1;var functionPointers=new Array(20);function addFunction(func,sig){var base=0;for(var i=base;i<base+20;i++){if(!functionPointers[i]){functionPointers[i]=func;return jsCallStartIndex+i}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}function removeFunction(index){functionPointers[index-jsCallStartIndex]=null}if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable;var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);ret=convertReturnValue(ret);if(stack!==0)stackRestore(stack);return ret}function cwrap(ident,returnType,argTypes,opts){argTypes=argTypes||[];var numericArgs=argTypes.every(function(type){return type==="number"});var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return function(){return ccall(ident,returnType,argTypes,arguments,opts)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(u8Array[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=u8Array[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|u8Array[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var DYNAMIC_BASE=17216,DYNAMICTOP_PTR=8768;var TOTAL_STACK=8192;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY<TOTAL_STACK)err("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){wasmMemory=new WebAssembly.Memory({"initial":TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=wasmMemory.buffer}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="data:application/octet-stream;base64,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eHFGBEAgACEBBQJAIAJBAEEZIAFBAXZrIAFBH0YbdCEEA0AgAEEQaiAEQR92QQJ0aiIGKAIAIgEEQCAEQQF0IQQgAiABKAIEQXhxRg0CIAEhAAwBCwsgBiADNgIAIAMgADYCGCADIAM2AgwgAyADNgIIDAILCyABQQhqIgAoAgAiAiADNgIMIAAgAzYCACADIAI2AgggAyABNgIMIANBADYCGAsFQcw4IAQgBnI2AgAgACADNgIAIAMgADYCGCADIAM2AgwgAyADNgIIC0HoOEHoOCgCAEF/aiIANgIAIAAEQA8LQZA8IQADQCAAKAIAIgNBCGohACADDQALQeg4QX82AgALDAAgACAAKAIEEIIBCxoAIABBADYCBCAAQQA2AgggACAAQQRqNgIACxkBAX8gACgCACIBBEAgACABNgIEIAEQMQsLbwEDfyMDIQQjA0EgaiQDIAQgASgCADYCACAEQQxqIgUgBCgCADYCACAAIAUgBEEIaiIGIARBBGogAhCvASICKAIAIgFFBEAgBSAAIAMQwQMgACAGKAIAIAIgBSgCABCDASAFKAIAIQELIAQkAyABCwoAIABB+A42AgALmwEBBn8jAyEFIwNBIGokAyAFIQNB/////wMgACgCBCAAKAIAIgJrQQJ1IgdBAWoiBkkEQBAABSADIAYgACgCCCACayIEQQF1IgIgAiAGSRtB/////wMgBEECdUH/////AUkbIAcgAEEIahDHASADQQhqIgQoAgAiAiABKAIANgIAIAQgAkEEajYCACAAIAMQxgEgAxDFASAFJAMLC6oCAQZ/IAFBb0sEQBAACyAAQQtqIgcsAAAiA0EASCIEBH8gACgCBCEFIAAoAghB/////wdxQX9qBSADQf8BcSEFQQoLIQIgBSABIAUgAUsbIgZBC0khAUEKIAZBEGpBcHFBf2ogARsiBiACRwRAAkACQAJAIAEEQCAAKAIAIQEgBAR/QQAhBCABIQIgAAUgACABIANB/wFxQQFqEEQaIAEQMQwDCyEBBSAGQQFqIgIQKyEBIAQEf0EBIQQgACgCAAUgASAAIANB/wFxQQFqEEQaIABBBGohAwwCCyECCyABIAIgAEEEaiIDKAIAQQFqEEQaIAIQMSAERQ0BIAZBAWohAgsgACACQYCAgIB4cjYCCCADIAU2AgAgACABNgIADAELIAcg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IAEsAAAiBEYEQCAAQQFqIQAgAUEBaiEBQQAgAkF/aiICRQ0CGgwBCwsgA0H/AXEgBEH/AXFrCwVBAAsLhQEBB38jAyEDIwNBMGokAyADQRBqIQQgA0EMaiEHIAMiBiADQSRqEDMgA0EEaiEIQbwUIQUDQCAHIAg2AgAgBCAHKAIANgIAIAYgBCAFIAUQNRogBUEEaiIFQcQURw0ACyAEEJIBIAYgAEECIAEgBCACEJEBIQkgBBCQASAGEDIgAyQDIAkLHwAgAQRAIAAgASgCABCCASAAIAEoAgQQggEgARAxCwtVACADQQA2AgAgA0EANgIEIAMgATYCCCACIAM2AgAgACgCACgCACIBBEAgACABNgIAIAIoAgAhAwsgACgCBCADENMDIABBCGoiACAAKAIAQQFqNgIAC6MBAQJ/IABBBGoiBCgCACEDIABBBGohACADBEACQCACKAIAIQQgACECIAMhAAJAAkADQAJAIAQgACgCECIDSQR/IAAoAgAiA0UNASAAIQIgAwUgAyAETw0EIABBBGoiAigCACIDRQ0DIAMLIQAMAQsLIAEgADYCAAwCCyABIAA2AgAgAiEADAELIAEgADYCACACIQALBSABIAQ2AgAgBCEACyAAC3IBA38jAyEDIwNBEGokAyABKAIAKAIIIQQgASAAQQIgAyIAIARBH3FBoAFqEQQABH9BAAUCfyAAQZIsQQIQgAFFBEAgAkEANgIAQQEMAQsgAEGULEECEIABBH9BAAUgAkEBNgIAQQELCwshBSADJAMgBQs1ACAAQQRqIQAgAiABayICQQBKBEAgACgCACABIAIQSRogACAAKAIAIAJBAnZBAnRqNgIACwspACAAQQA2AgAgAEEANgIEIABBADYCCCABBEAgACABEGwgACABEOADCwsVACAALwEAQYAETCAALwECQYAETHEL+QMBCn8jAyEHIwNBMGokAyAHQRRqIQUgB0EIaiEGIAdBBGohBCAHIgNBJGoiCEEANgIAIANBIGoiCUEANgIAAn8CQAJAAkAgAEGBBBAqRQ0AIABBggQQKkUNACAAQYEEIAkQMARAIABBggQgCBAwDQILQQAMAwsgAEGRAhAqBEAgAEGXAhAqBEAgBUEANgIAIAVBADYCBCAFQQA2AgggBkEANgIAIAZBBGoiCkEANgIAIAZBADYCCAJ/IABB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NgIAIAEQNiABQZwSNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGAEjYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFB5BE2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQcgRNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGsETYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFBkBE2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQfQQNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUHYEDYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFBvBA2AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQaAQNgIAIAMgATYCACACKAIAIgQgBSgCAEkEQCAEIAE2AgAgAiACKAIAQQRqNgIABSAAIAMQNwtBBBArIgFBADYCACABEDYgAUGEEDYCACADIAE2AgAgAigCACIEIAUoAgBJBEAgBCABNgIAIAIgAigCAEEEajYCAAUgACADEDcLQQQQKyIBQQA2AgAgARA2IAFB6A82AgAgAyABNgIAIAIoAgAiBCAFKAIASQRAIAQgATYCACACIAIoAgBBBGo2AgAFIAAgAxA3C0EEECsiAUEANgIAIAEQNiABQcwPNgIAIAMgATYCACAC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AgQgBBAtIAoQLUEAIAMoAgAgAEEDdGooAgRFDQIaCyAAQQFqIgAgCygCAEkNAAsgCCgCAEUNAUEACwwBCyACIANHBEAgAiADKAIAIAMoAgQQ5QMLQQELIQAgAxA0IAcQLQVBACEACwVBACEACyAGJAMgAAvOAQEGfyAAKAIAIgMhBSACIAEiBmsiBCAAQQhqIgcoAgAgA2tLBEAgABCLAUH/////ByAESQRAEAAFIAAgBCAHKAIAIAAoAgBrIgNBAXQiBSAFIARJG0H/////ByADQf////8DSRsQkwEgACABIAIgBBCMAQsFIAQgAEEEaiIHKAIAIANrIghLIQMgASAIaiACIAMbIgggBmsiBgRAIAUgASAGEHoaCyADBEAgACAIIAIgACgCACAEIAcoAgBrahCMAQUgByAFIAZqNgIACwsLCQAgAEEMahA0C0sBBH8gACgCACIBBEACfyABIABBBGoiAygCACICRgR/IAEFA0AgAkFYaiICEDsgASACRw0ACyAAKAIACyEEIAMgATYCACAECxAxCwsyAQF/IABBBGoiAigCACEAA0AgAEEAOgAAIAIgAigCAEEBaiIANgIAIAFBf2oiAQ0ACwsHACAAKAIIC0IBA38gACgCBCICIABBCGoiAygCACIBRwRAIAMgAUF8aiACa0ECdkF/c0ECdCABajYCAAsgACgCACIABEAgABAxCwu2AQEFfyABQQRqIgIoAgBBACAAQQRqIgUoAgAgACgCACIEayIGQQJ1a0ECdGohAyACIAM2AgAgBkEASgR/IAMgBCAGEEkaIAIhBCACKAIABSACIQQgAwshAiAAKAIAIQMgACACNgIAIAQgAzYCACAFKAIAIQMgBSABQQhqIgIoAgA2AgAgAiADNgIAIABBCGoiACgCACECIAAgAUEMaiIAKAIANgIAIAAgAjYCACABIAQoAgA2AgALXgECfyAAQQxqIgVBADYCACAAIAM2AhAgAQRAIAFB/////wNLBEAQAAUgAUECdBArIQQLCyAAIAQ2AgAgACACQQJ0IARqIgI2AgggACACNgIEIAUgAUECdCAEajYCAAs7AQJ/IwMhASMDQRBqJAMgAUIANwIAIAFBADYCCCABQa8iQQYQKCAAQaAfIAEQPiECIAEQJyABJAMgAguTAgEHfyMDIQIjA0FAayQDIAJBMGoiBUIA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EgBBDSAAIAEgAiADIAQgBRABCxIAQQwgACABIAIgAyAEIAUQAQsSAEELIAAgASACIAMgBCAFEAELEgBBCiAAIAEgAiADIAQgBRABCxIAQQkgACABIAIgAyAEIAUQAQsSAEEIIAAgASACIAMgBCAFEAELEgBBByAAIAEgAiADIAQgBRABCxIAQQYgACABIAIgAyAEIAUQAQsSAEEFIAAgASACIAMgBCAFEAELEgBBBCAAIAEgAiADIAQgBRABCxIAQQMgACABIAIgAyAEIAUQAQsSAEECIAAgASACIAMgBCAFEAELEgBBASAAIAEgAiADIAQgBRABCxIAQQAgACABIAIgAyAEIAUQAQsQAEETIAAgASACIAMgBBACCxAAQRIgACABIAIgAyAEEAILEABBESAAIAEgAiADIAQQAgsQAEEQIAAgASACIAMgBBACCxAAQQ8gACABIAIgAyAEEAILEABBDiAAIAEgAiADIAQQAgsQAEENIAAgASACIAMgBBACCxAAQQwgACABIAIgAyAEEAILEABBCyAAIAEgAiADIAQQAgsQAEEKIAAgASACIAMgBBACCxAAQQkgACABIAIgAyAEEAILEABBCCAAIAEgAiADIAQQAgsQAEEHIAAgASACIAMgBBACCxAAQQYgACABIAIgAyAEEAILEABBBSAAIAEgAiADIAQQAgsQAEEEIAAgASACIAMgBBACCxAAQQMgACABIAIgAyAEEAILEABBAiAAIAEgAiADIAQQAgsQAEEBIAAgASACIAMgBBACCxAAQQAgACABIAIgAyAEEAILDgBBEyAAIAEgAiADEAMLDgBBEiAAIAEgAiADEAMLDgBBESAAIAEgAiADEAMLDgBBECAAIAEgAiADEAMLLgEBfyMDIQMjA0EQaiQDIAMgADYCACADIAEQnAEQnQEgAyACEJwBEJ0BIAMkAwsOAEEPIAAgASACIAMQAwsOAEEOIAAgASACIAMQAwsOAEENIAAgASACIAMQAwsOAEEMIAAgASACIAMQAwsOAEELIAAgASACIAMQAwsOAEEKIAAgASACIAMQAwsOAEEJIAAgASACIAMQAwsOAEEIIAAgASACIAMQAwsOAEEHIAAgASACIAMQAwsOAEEGIAAgASACIAMQAwt6AQV/IwMhBCMDQRBqJANBuDgsAABFBEBBuDgsAABBAUYEf0EABUG4OEEBOgAAQQELBEACfyMDIQcjA0EQaiQDQQNBrA4QHiEGIAcLJANBwDggBjYCAAsLAn9BwDgoAgAhCCAEIAIgAxCOAiAICyAAIAEgBBAgIAQkAwsOAEEFIAAgASACIAMQAwsOAEEEIAAgASACIAMQAwsOAEEDIAAgASACIAMQAwsOAEECIAAgASACIAMQAwsOAEEBIAAgASACIAMQAwsOAEEAIAAgASACIAMQAwsIAEETIAAQBAsIAEESIAAQBAsIAEERIAAQBAsmAQF/IwMhAiMDQRBqJAMgAiABEJ4BIABBwA0gAhAPNgIAIAIkAwsIAEEQIAAQBAsIAEEPIAAQBAsIAEEOIAAQBAsIAEENIAAQBAsIAEEMIAAQBAsIAEELIAAQBAsIAEEKIAAQBAsIAEEJIAAQBAsIAEEIIAAQBAsIAEEHIAAQBAsyAQF/IwMhAyMDQRBqJAMgASgCACEBIAMgAhCjAiAAIAEgAygCABAdEG4gAxAsIAMkAwsIAEEGIAAQBAsIAEEFIAAQBAsIAEEEIAAQBAsIAEEDIAAQBAsIAEECIAAQBAsIAEEBIAAQBAsIAEEAIAAQBAsQACABIABBP3FB4AFqEQAACwYAQRMQBQsGAEESEAULSwIBfwJ8IwMhASMDQRBqJAMCfCAAKAIAQagOKAIAIAFBBGoiABAhIQMgASAAKAIANgIAIAEoAgAQGiABJAMgAwtEAAAAAAAAAABiCwYAQREQBQsGAEEQEAULBgBBDxAFCwYAQQ4QBQsGAEENEAULBgBBDBAFCwYAQQsQBQsGAEEKEAULBgBBCRAFCwYAQQgQBQs0AQF/IwMhAiMDQRBqJAMgAiAANgIAIAIoAgAgAS8BADYCACACIAIoAgBBCGo2AgAgAiQDCwYAQQcQBQsGAEEGEAULBgBBBRAFCwYAQQQQBQsGAEEDEAULBgBBAhAFCwYAQQEQBQsGAEEAEAULDgAgAEEfcUHAAWoRBQALDgBBEyAAIAEgAiADEAYLDgBBEiAAIAEgAiADEAYLDgBBESAAIAEgAiADEAYLDgBBECAAIAEgAiADEAYLDgBBDyAAIAEgAiADEAYLDgBBDiAAIAEgAiADEAYLDgBBDSAAIAEgAiADEAYLDgBBDCAAIAEgAiADEAYLDgBBCyAAIAEgAiADEAYLDgBBCiAAIAEgAiADEAYLDgBBCSAAIAEgAiADEAYLDgBBCCAAIAEgAiADEAYLDgBBByAAIAEgAiADEAYLDgBBBiAAIAEgAiADEAYLDgBBBSAAIAEgAiADEAYLDgBBBCAAIAEgAiADEAYLDgBBAyAAIAEgAiADEAYLDgBBAiAAIAEgAiADEAYLDgBBASAAIAEgAiADEAYLDgBBACAAIAEgAiADEAYLDABBEyAAIAEgAhAHCwwAQRIgACABIAIQBwsMAEERIAAgASACEAcLDABBECAAIAEgAhAHCwwAQQ8gACABIAIQBwsMAEEOIAAgASACEAcLDABBDSAAIAEgAhAHCwwAQQwgACABIAIQBwsMAEELIAAgASACEAcLDABBCiAAIAEgAhAHCwwAQQkgACABIAIQBwsMAEEIIAAgASACEAcLDABBByAAIAEgAhAHCwwAQQYgACABIAIQBwsMAEEFIAAgASACEAcLDABBBCAAIAEgAhAHCwwAQQMgACABIAIQBwsMAEECIAAgASACEAcLDABBASAAIAEgAhAHCwwAQQAgACABIAIQBwsKAEETIAAgARAICwoAQRIgACABEAgLCgBBESAAIAEQCAsKAEEQIAAgARAICwoAQQ8gACABEAgLCgBBDiAAIAEQCAsKAEENIAAgARAICwoAQQwgACABEAgLCgBBCyAAIAEQCAsKAEEKIAAgARAICwoAQQkgACABEAgLCgBBCCAAIAEQCAsKAEEHIAAgARAICwoAQQYgACABEAgLCgBBBSAAIAEQCAsKAEEEIAAgARAICwoAQQMgACABEAgLCgBBAiAAIAEQCAsKAEEBIAAgARAICwoAQQAgACABEAgLCABBEyAAEAkLCABBEiAAEAkLCABBESAAEAkLCABBECAAEAkLCABBDyAAEAkLCABBDiAAEAkLCABBDSAAEAkLCABBDCAAEAkLCABBCyAAEAkLCABBCiAAEAkLCABBCSAAEAkLCABBCCAAEAkLCABBByAAEAkLCABBBiAAEAkLCABBBSAAEAkLCABBBCAAEAkLCABBAyAAEAkLCABBAiAAEAkLCABBASAAEAkLCABBACAAEAkLWAEDfyAALAALIgFBAEgEfyAAKAIEIgJBBGoQYSIBIAI2AgAgACgCACEDIAIFIAFB/wFxIgJBBGoQYSIBIAI2AgAgACEDIAILIQAgAUEEaiADIAAQSRogAQtzAQJ/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NgIEIABBADYCCCABQQRqIgMoAgAgASgCAGsiBEEobSECIAQEQCAAIAIQxgMgACABKAIAIAMoAgAQxQMLC0wBA38gAEEANgIAIABBADYCBCAAQQA2AgggAUEEaiIDKAIAIAEoAgBrIgRBAnUhAiAEBEAgACACEGwgACABKAIAIAMoAgAgAhCGAQsLbwEDfyMDIQQjA0EgaiQDIAQgASgCADYCACAEQQxqIgUgBCgCADYCACAAIAUgBEEIaiIGIARBBGogAhCvASICKAIAIgFFBEAgBSAAIAMQxAMgACAGKAIAIAIgBSgCABCDASAFKAIAIQELIAQkAyABC2ABBH8gACABKAIANgIAIAAgASgCBCIDNgIEIAAgAUEIaiIEKAIAIgU2AgggAEEEaiECIAUEQCADIAI2AgggASABQQRqIgA2AgAgAEEANgIAIARBADYCAAUgACACNgIACwvhAQEDfyAAIAEQygMgAEEMaiICQQA2AgAgAEEQaiIDQQA2AgAgAEEUaiIEQQA2AgAgAiABQQxqIgIoAgA2AgAgAyABQRBqIgMoAgA2AgAgBCABQRRqIgQoAgA2AgAgBEEANgIAIANBADYCACACQQA2AgAgAEEYaiICQQA2AgAgAEEcaiIDQQA2AgAgAEEgaiIEQQA2AgAgAiABQRhqIgIoAgA2AgAgAyABQRxqIgMoAgA2AgAgBCABQSBqIgQoAgA2AgAgBEEANgIAIANBADYCACACQQA2AgAgACABKAIkNgIkC0gBA38gACgCBCIDIABBCGoiAigCACIBRwRAA0AgAiABQVhqIgE2AgAgARA7IAIoAgAiASADRw0ACwsgACgCACIABEAgABAxCwvFAQEGfyABQQRqIQIgACgCACIFIABBBGoiBigCACIERgR/IAAhByACKAIAIQMgAgUgAigCACEDA0AgA0FYaiAEQVhqIgQQywMgAiACKAIAQVhqIgM2AgAgBCAFRw0ACyAAIQcgACgCACEFIAILIQQgByADNgIAIAQgBTYCACAGKAIAIQMgBiABQQhqIgIoAgA2AgAgAiADNgIAIABBCGoiACgCACEDIAAgAUEMaiIAKAIANgIAIAAgAzYCACABIAQoAgA2AgALXQECfyAAQQxqIgVBADYCACAAIAM2AhAgAQRAIAFB5syZM0sEQBAABSABQShsECshBAsLIAAg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AEEgaiEBA0AgARBVIAFBCGoiASACRw0ACyAAQQA6ADggAEE8ahBVIABB3ABqIQIgAEHEAGohAQNAIAEQVSABQQhqIgEgAkcNAAsgAEHcAGoiAEIANwIAIABBADYCCAtgAQN/IwMhAyMDQRBqJAMgA0EEaiIEQQA2AgAgAyIFQQA2AgAgACAEIAMQugEEQCAEKAIAIgRB//8DTQRAIAUoAgAiAEH//wNNBEAgASAEOwEAIAIgADsBAAsLCyADJAMLNQACQAJAAkAgAigCDA4CAQACCyAAIAIgAkECahDdAwwBCyACKAIIIAEgAiACQQJqELgBGgsLhgEBA38gASgCACICIQMgASgCBCACa0ECdSAAKAIEIgEgACgCACICa0ECdSIARyABIAJGcgR/QQAFIABBf2oiBAR/An9BACEAIAIoAgAhAQN/QQAgAEECdCADaigCACABaiIBIABBAWoiAEECdCACaigCAEcNARogACAESQ0AQQELCwVBAQsLCygBAX8gAEEEaiIAKAIAIgJBACABQQJ0EKMBGiAAIAFBAnQgAmo2AgAL6QEBB38gACgCACIFIQkgAiABIgZrQQJ1IgMgAEEIaiIEKAIAIAVrQQJ1SwRAIAAQiwFB/////wMgA0kEQBAABSAAIAMgBCgCACAAKAIAayIFQQF1IgQgBCADSRtB/////wMgBUECdUH/////AUkbEGwgACABIAIgAxCGAQsFIAMgAEEEaiIEKAIAIAVrQQJ1IgdLIQggB0ECdCABaiACIAgbIgcgBmsiBgRAIAUgASAGEHoaCyAGQQJ1IQEgCARAIAAgByACIAMgBCgCACAAKAIAa0ECdWsQhgEFIAQgAUECdCAJajYCAAsLCwQAIwMLwgEBBX8jAyEHIwNBEGokAyACKAIAIgUgASgCACICayIDQW9LBEAQAAsgA0ELSQRAIAAgAzoACwUgACADQRBqQXBxIgYQKyIENgIAIAAgBkGAgICAeHI2AgggACADNgIEIAQhAAsgByEDIAUgAiIERwRAIAUgAmshBiAAIQIDQCADIAQsAAA6AAAgAiADECkgASAEQQFqIgQ2AgAgAkEBaiECIAQgBUcNAAsgACAGaiEACyADQQA6AAAgACADECkgByQDCzIBAX8gAEEEaiICKAIAIQADQCAAQgA3AgAgAiACKAIAQQhqIgA2AgAg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IAAgACgCACgCDEE/cREBACAEED4EQCAFQgA3AgAgBUEANgIIIAVBnShBCBAoIAJCADcDACACQQA2AgggAkGmKEECECggAkEMaiIBQgA3AgAgAUEANgIIIAFBqShBAhAoIAJBGGoiAUIANwMAIAFBADYCCCABQawoQQIQKCACQSRqIgFCADcCACABQQA2AgggAUGvKEECECggAkEwaiIBQgA3AwAgAUEANgIIIAFBsihBAhAoQQAhAQNAIAEEQEEBIQEFIAAgACgCACgCDEE/cREBACEBIAcgBSAIQQxsIAJqEP0DIAMgASAHED4hASAHECcLIAhBAWoiCEEFRw0ACyACQTxqIQADQCAAQXRqIgAQJyAAIAJHDQALIAUQJwVBACEBCyAEECcFQQAhAQsFQQAhAQsFQQAhAQsgAxAtIAYkAyABCwQAQQELdgEDfyMDIQQjA0FAayQDIAQiAkEMaiIDIAAgARA6IAMgAkE4aiIAED8EQCADIAAsAABBAEdBKhBLBEAgAkIANwIAIAJBADYCCCACQdUnQQQQKCADQQggAhBHIQAgAhAnBUEAIQALBUEAIQALIAMQLSAEJAMgAAsEAEECC5ECAQZ/IwMhBCMDQdAAaiQDIARBxQBqIQUgBEEMaiEDIAQiAkEYaiIGIAAgARA6IAYgAkHEAGoiABA/BEAgA0IANwIAIANBADYCCCAALAAABEAgAkIANwIAIAJBADYCCCACQYUnQQgQKAUgAkIANwIAIAJBADYCCCACQY4nQY4nEFEQKAsgA0ELaiIALAAAQQBIBEACfyADKAIAIQcgBUEAOgAAIAcLIAUQKSADQQA2AgQFIAVBADoAACADIAUQKSAAQQA6AAALIANBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgBkEGIAMQRyEAIAMQJwVBACEACyAGEC0gBCQDIAALBABBAwurBQELfyMDIQgjA0HgAGokAyAIQd0AaiEEIAhBJGohBiAIIgNBGGohAiADQTBqIgcgACABEDogByADQdwAaiIBED8EQCAGQgA3AgAgBkEANgIIIAZBoiZBEBAoIAdBECAGEEcEf0EBBSADQgA3AwAgA0IANwMIIANCADcDECABLAAABEAgAkIANwIAIAJBADYCCCACQbMmQQQQKCADQQtqIgEsAABBAEgEQAJ/IAMoAgAhCSAEQQA6AAAgCQsgBBApIANBADYCBAUgBEEAOgAAIAMgBBApIAFBADoAAAsgA0EAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBuCZBBBAoBSACQgA3AgAgAkEANgIIIAJBvSZBBBAoIANBC2oiASwAAEEASARAAn8gAygCACEKIARBADoAACAKCyAEECkgA0EANgIEBSAEQQA6AAAgAyAEECkgAUEAOgAACyADQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHCJkEEECgLIANBDGoiBUELaiIBLAAAQQBIBH8CfyAFKAIAIQsgBEEAOgAAIAsLIAQQKSADQQA2AhAgBQUgBEEAOgAAIAUgBBApIAFBADoAACAFCyEBIAVBABA4IAEgAikCADcCACABIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgACgCACgCDCEBIAcgACABQT9xEQEAIAMQPgR/IAAoAgAoAgwhASAHIAAgAUE/cREBACADQQxqED4FQQALIQwgA0EYaiEAA0AgAEF0aiIAECcgACADRw0ACyAMCyEAIAYQJwVBACEACyAHEC0gCCQDIAALjQwBEX8jAyEGIwNBgAFqJAMgBkH1AGohBSAGIgRBPGohAiAEQcgAaiIHIAAgARA6IAcgBEH0AGoiARA/BH8gBEIANwMAIARCADcDCCAEQgA3AxAgBEIANwMYIARCADcDICAEQgA3AyggBEIANwMwIARBADYCOCABLAAABEAgAkIANwIAIAJBADYCCCACQZ4lQQgQKCAEQQtqIgEsAABBAEgEQAJ/IAQoAgAhCCAFQQA6AAAgCAsgBRApIARBADYCBAUgBUEAOgAAIAQgBRApIAFBADoAAAsgBEEAEDggBCACKQIANwIAIAQgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBpyVBCBAoIARBDGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIQkgBUEAOgAAIAkLIAUQKSAEQQA2AhAgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQbAlQQQQKCAEQRhqIgFBC2oiAywAAEEASAR/An8gASgCACEKIAVBADoAACAKCyAFECkgBEEANgIcIAEFIAVBADoAACABIAUQKSADQQA6AAAgAQshAyABQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkG1JUECECggBEEkaiIBQQtqIgMsAABBAEgEfwJ/IAEoAgAhCyAFQQA6AAAgCwsgBRApIARBADYCKCABBSAFQQA6AAAgASAFECkgA0EAOgAAIAELIQMgAUEAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJBuCVBCBAoBSACQgA3AgAgAkEANgIIIAJBwSVBCBAoIARBC2oiASwAAEEASARAAn8gBCgCACEMIAVBADoAACAMCyAFECkgBEEANgIEBSAFQQA6AAAgBCAFECkgAUEAOgAACyAEQQAQOCAEIAIpAgA3AgAgBCACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHKJUEIECggBEEMaiIBQQtqIgMsAABBAEgEfwJ/IAEoAgAhDSAFQQA6AAAgDQsgBRApIARBADYCECABBSAFQQA6AAAgASAFECkgA0EAOgAAIAELIQMgAUEAEDggAyACKQIANwIAIAMgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyACQgA3AgAgAkEANgIIIAJB0yVBBBAoIARBGGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIQ4gBUEAOgAAIA4LIAUQKSAEQQA2AhwgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQdglQQIQKCAEQSRqIgFBC2oiAywAAEEASAR/An8gASgCACEPIAVBADoAACAPCyAFECkgBEEANgIoIAEFIAVBADoAACABIAUQKSADQQA6AAAgAQshAyABQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHbJUEIECgLIARBMGoiAUELaiIDLAAAQQBIBH8CfyABKAIAIRAgBUEAOgAAIBALIAUQKSAEQQA2AjQgAQUgBUEAOgAAIAEgBRApIANBADoAACABCyEDIAFBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcCfyAEQTxqIRFBACEDQQAhAQNAIAAoAgAoAgwhBSADIAcgACAFQT9xEQEAIAFBDGwgBGoQPkEBcWohAyABQQFqIgFBBUcNAAsgEQshAANAIABBdGoiABAnIAAgBEcNAAsgA0EBSwVBAAshEiAHEC0gBiQDIBILBQBBgAgLegEEfyMDIQIjA0EQaiQDIAJBBGohAyACIQQgAkEMaiIFIABBf2o2AgAgAkEIaiIAQckZEB8QbiAAELkCBEAgAxB5IAQgACAFEK4CIAQoAgBB/hkgAyABEJkCIAQQLCADECwgABAsIAIkAwVB2hlB+RZBxABB7xkQCwsLBABBBQv3BAEJfyMDIQcjA0HgAGokAyAHQdEAaiEEIAciA0EYaiECIANBJGoiBiAAIAEQOiAGIANB0ABqIgEQPwRAIANCADcDACADQgA3AwggA0IANwMQIAEsAAAEQCACQgA3AgAgAkEANgIIIAJBzCRBBBAoIANBC2oiASwAAEEASARAAn8gAygCACEIIARBADoAACAICyAEECkgA0EANgIEBSAEQQA6AAAgAyAEECkgAUEAOgAACyADQQAQOCADIAIpAgA3AgAgAyACKAIINgIIIAJCADcCACACQQA2AgggAhAnIAJCADcCACACQQA2AgggAkHRJEEEECgFIAJCADcCACACQQA2AgggAkHWJEEEECggA0ELaiIBLAAAQQBIBEACfyADKAIAIQkgBEEAOgAAIAkLIAQQKSADQQA2AgQFIARBADoAACADIAQQKSABQQA6AAALIANBABA4IAMgAikCADcCACADIAIoAgg2AgggAkIANwIAIAJBADYCCCACECcgAkIANwIAIAJBADYCCCACQdskQQQQKAsgA0EMaiIBQQtqIgUsAABBAEgEfwJ/IAEoAgAhCiAEQQA6AAAgCgsgBBApIANBADYCECABBSAEQQA6AAAgASAEECkgBUEAOgAAIAELIQQgAUEAEDggBCACKQIANwIAIAQgAigCCDYCCCACQgA3AgAgAkEANgIIIAIQJyAAKAIAKAIMIQEgBiAAIAFBP3ERAQAgAxA+BH8gACgCACgCDCEBIAYgACABQT9xEQEAIANBDGoQPgVBAAshASADQRhqIQADQCAAQXRqIgAQJyAAIANHDQALBUEAIQELIAYQLSAHJAMgAQsEAEEGC3ABA38jAyEDIwNBQGskAyADIgJBDGoiBCAAIAEQOiABIAJBOGoQPwRAIAJCADcCACACQQA2AgggAkGFJEEIECggACgCACgCDCEBIAQgACABQT9xEQEAIAIQPiEAIAIQJwVBACEACyAEEC0gAyQDIAALBABBBwteAQR/IwMhAiMDQUBrJAMgACgCACgCDCEEIAJBDGoiAyABIAAgBEE/cREBABCXASACQgA3AgAgAkEANgIIIAJBwiNBBBAoIANBACACEEchBSACECcgAxAtIAIkAyAFC2EBA38jAyEDIwNBMGokAyADIgIgACABEDogAiACQSxqIgAQPwR/IAIgACwAAEEARyIAQSoQSwR/IAIgABDJAQR/IAIQyAFBAXMFQQALBUEACwVBAAshBCACEC0gAyQDIAQLBABBCQtpAQF/IwMhAiMDQRBqJAMgABBnIAIgAUEwahCwASAAQf8XIAIQPCACECwgAiABQTxqELABIABBhRggAhA8IAIQLCACIAEQuAMgACACEL4DIAIQLCACIAEQsAMgACACELUDIAIQLCACJAMLSwEEfyAAKAIAIgEEQAJ/IAEgAEEEaiIDKAIAIgJGBH8gAQUDQCACQXRqIgIQJyABIAJHDQALIAAoAgALIQQgAyABNgIAIAQLEDELCz4BAX8gAEEEaiEDIAEgAkcEQCADKAIAIQADQCAAIAEQfyADIAMoAgBBDGoiADYCACABQQxqIgEgAkcNAAsLC2oBA38jAyEDIwNBMGokAyADIgIgACABEDogAiACQSxqIgAQPwR/IAIgACwAAEEARyIAQSoQSwR/IAIgABBmBH8gAiAAEMkBBH8gAhDIAQVBAAsFQQALBUEACwVBAAshBCACEC0gAyQDIAQLBABBCgueAQEDfyMDIQQjA0FAayQDIAQiAkEMaiIDIAAgARA6IAMgAkE4aiIBED8EQAJAIAMgASwAAEEARyIBQdKeARBLRQRAIAMgAUHSpgEQS0UEQEEAIQAM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SW50ZXJmYWNlRQBtYWtlcgBtb2RlbABwcmV2aWV3AHRodW1ibmFpbABOU3QzX18yMTJiYXNpY19zdHJpbmdJY05TXzExY2hhcl90cmFpdHNJY0VFTlNfOWFsbG9jYXRvckljRUVFRQBOU3QzX18yMjFfX2Jhc2ljX3N0cmluZ19jb21tb25JTGIxRUVFAGNvbG9yU3BhY2UAb3JpZW50YXRpb24AZm9ybWF0AG9mZnNldABsZW5ndGgAd2lkdGgAaGVpZ2h0AGFkb2JlUmdiAHNSZ2IAZnVuY3Rpb25Qb2ludGVycwBjYWxsYmFja3MuYXM8Ym9vbD4oKQBDYWxsYmFja1Jlc3VsdABjYWxsAE4xMGVtc2NyaXB0ZW4zdmFsRQBONHBpZXgxMmJpbmFyeV9wYXJzZTE0UGFnZWRCeXRlQXJyYXlFACFjaGVja2Vyc18uZW1wdHkoKQBub2RlX21vZHVsZXMvcGlleC9zcmMvaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbi9pbWFnZV90eXBlX3JlY29nbml0aW9uX2xpdGUuY2MAUmVxdWVzdGVkU2l6ZQBhAENvbXBhcmUAYgBGT1ZiAHBvc19pbl9wYWdlIDwgY3VycmVudF9wYWdlX2xlbl8Abm9kZV9tb2R1bGVzL3BpZXgvc3JjL2JpbmFyeV9wYXJzZS9yYW5nZV9jaGVja2VkX2J5dGVfcHRyLmgAb3BlcmF0b3JbXQBvZmZzZXQgPj0gc3ViX2FycmF5X2JlZ2luXyAmJiBvZmZzZXQgPCBzdWJfYXJyYXlfZW5kXwBub2RlX21vZHVsZXMvcGlleC9zcmMvYmluYXJ5X3BhcnNlL3JhbmdlX2NoZWNrZWRfYnl0ZV9wdHIuY2MAbG9hZFBhZ2VGb3JPZmZzZXQAZmFsc2UAcmVtYWluaW5nTGVuZ3RoAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRYM2ZUeXBlQ2hlY2tlckUATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExMVR5cGVDaGVja2VyRQBTQU1TVU5HAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRTcndUeXBlQ2hlY2tlckUATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFJ3MlR5cGVDaGVja2VyRQBBUkVDT1lLAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMjFSYXdDb250YXhOVHlwZUNoZWNrZXJFAEZVSklGSUxNAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRSYWZUeXBlQ2hlY2tlckUAcWt0awAAAAgAcWt0bgAAAAgATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFF0a1R5cGVDaGVja2VyRQBBT0MATU0AUEVOVEFYIAAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNFBlZlR5cGVDaGVja2VyRQBPTFlNUABONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0T3JmVHlwZUNoZWNrZXJFAE5SVyAgIABOSUtPTgACFAAFABQCBQAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNE5yd1R5cGVDaGVja2VyRQBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0TmVmVHlwZUNoZWNrZXJFAABNUk0ATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNE1yd1R5cGVDaGVja2VyRQBQS1RTAAAAAQBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0TW9zVHlwZUNoZWNrZXJFAPoNAAEA+gAAAgAN+gEAAAD6AgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNEtkY1R5cGVDaGVja2VyRQDGEgABAAAABADGEwABAAAABADGFAACAMYgAMYtAAQAAAABABLGAQAEAAAAABPGAQAEAAAAABTGAgAAIMYALcYEAAEAAAAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNERuZ1R5cGVDaGVja2VyRQBLT0RBSyAgICAgICAgICAgAAPpAAIADOUAAgDpAwIAAOUMAgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNERjclR5cGVDaGVja2VyRQAAELqwrLsAAgBIRUFQQ0NEUgBONHBpZXgyMmltYWdlX3R5cGVfcmVjb2duaXRpb24xMl9HTE9CQUxfX05fMTE0Q3J3VHlwZUNoZWNrZXJFAENSAgAATjRwaWV4MjJpbWFnZV90eXBlX3JlY29nbml0aW9uMTJfR0xPQkFMX19OXzExNENyMlR5cGVDaGVja2VyRQBTT05ZAACwAQAEAAAAAAIAAAMAAAMBAAMCAAMDAE40cGlleDIyaW1hZ2VfdHlwZV9yZWNvZ25pdGlvbjEyX0dMT0JBTF9fTl8xMTRBcndUeXBlQ2hlY2tlckUAYXJyYXkAUmFuZ2VDaGVja2VkQnl0ZVB0cgBOU3QzX18yMTRkZWZhdWx0X2RlbGV0ZUlONHBpZXgxMmJpbmFyeV9wYXJzZTEyX0dMT0JBTF9fTl8xMjBNZW1vcnlQYWdlZEJ5dGVBcnJheUVFRQBOU3QzX18yMjBfX3NoYXJlZF9wdHJfcG9pbnRlcklQTjRwaWV4MTJiaW5hcnlfcGFyc2UxMl9HTE9CQUxfX05fMTIwTWVtb3J5UGFnZWRCeXRlQXJyYXlFTlNfMTRkZWZhdWx0X2RlbGV0ZUlTNF9FRU5TXzlhbGxvY2F0b3JJUzRfRUVFRQBONHBpZXgxMmJpbmFyeV9wYXJzZTEyX0dMT0JBTF9fTl8xMjBNZW1vcnlQYWdlZEJ5dGVBcnJheUUAU2l6ZU9mVHlwZSh0eXBlLCBOVUxMICkgKiBjb3VudCA9PSB2YWx1ZS5zaXplKCkAbm9kZV9tb2R1bGVzL3BpZXgvc3JjL3RpZmZfZGlyZWN0b3J5L3RpZmZfZGlyZWN0b3J5LmNjAEFkZEVudHJ5AElJTU12b2lkAGJvb2wAc3RkOjpzdHJpbmcAc3RkOjpiYXNpY19zdHJpbmc8dW5zaWduZWQgY2hhcj4Ac3RkOjp3c3RyaW5nAGVtc2NyaXB0ZW46OnZhbABlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxzaWduZWQgY2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dW5zaWduZWQgY2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8c2hvcnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIHNob3J0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIGludD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50MTZfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50MzJfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dWludDMyX3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGxvbmcgZG91YmxlPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0llRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8ZG91YmxlPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lkRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8ZmxvYXQ+AE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWZFRQBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBsb25nPgBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0ltRUUAZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8bG9uZz4ATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJbEVFAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWpFRQBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lpRUUATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJdEVFAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SXNFRQBOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0loRUUATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJYUVFAGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGNoYXI+AE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWNFRQBOU3QzX18yMTJiYXNpY19zdHJpbmdJd05TXzExY2hhcl90cmFpdHNJd0VFTlNfOWFsbG9jYXRvckl3RUVFRQBOU3QzX18yMTJiYXNpY19zdHJpbmdJaE5TXzExY2hhcl90cmFpdHNJaEVFTlNfOWFsbG9jYXRvckloRUVFRQBkb3VibGUAZmxvYXQAdW5zaWduZWQgbG9uZwBsb25nAHVuc2lnbmVkIGludABpbnQAdW5zaWduZWQgc2hvcnQAc2hvcnQAdW5zaWduZWQgY2hhcgBzaWduZWQgY2hhcgBjaGFyAE5TdDNfXzIxNF9fc2hhcmVkX2NvdW50RQBOU3QzX18yMTlfX3NoYXJlZF93ZWFrX2NvdW50RQBOMTBfX2N4eGFiaXYxMTZfX3NoaW1fdHlwZV9pbmZvRQBTdDl0eXBlX2luZm8ATjEwX19jeHhhYml2MTIwX19zaV9jbGFzc190eXBlX2luZm9FAE4xMF9fY3h4YWJpdjExN19fY2xhc3NfdHlwZV9pbmZvRQBOMTBfX2N4eGFiaXYxMjNfX2Z1bmRhbWVudGFsX3R5cGVfaW5mb0UAdgBjAGgAcwB0AGkAagBsAG0AZgBkAE4xMF9fY3h4YWJpdjEyMV9fdm1pX2NsYXNzX3R5cGVfaW5mb0U=";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}var binary=tryParseAsDataURI(wasmBinaryFile);if(binary){return binary}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(env){var info={"env":env,"global":{"NaN":NaN,Infinity:Infinity},"global.Math":Math,"asm2wasm":asm2wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asm"]=function(global,env,providedBuffer){env["memory"]=wasmMemory;env["table"]=wasmTable=new WebAssembly.Table({"initial":384,"maximum":384,"element":"anyfunc"});env["__memory_base"]=1024;env["__table_base"]=0;var exports=createWasm(env);return exports};__ATINIT__.push({func:function(){__GLOBAL__sub_I_bind_cpp()}});function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function ___cxa_pure_virtual(){ABORT=true;throw"Pure virtual function called!"}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return function(){"use strict";return body.apply(this,arguments)}}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(heap["buffer"],data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var endChar=HEAPU8[value+4+length];var endCharSwap=0;if(endChar!=0){endCharSwap=endChar;HEAPU8[value+4+length]=0}var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0){var stringSegment=UTF8ToString(decodeStartPtr);if(str===undefined)str=stringSegment;else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}if(endCharSwap!=0)HEAPU8[value+4+length]=endCharSwap}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var getHeap,shift;if(charSize===2){getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var HEAP=getHeap();var length=HEAPU32[value>>2];var a=new Array(length);var start=value+4>>shift;for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAP[start+i])}_free(value);return a.join("")},"toWireType":function(destructors,value){var HEAP=getHeap();var length=value.length;var ptr=_malloc(4+length*charSize);HEAPU32[ptr>>2]=length;var start=ptr+4>>shift;for(var i=0;i<length;++i){HEAP[start+i]=value.charCodeAt(i)}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){function testGlobal(obj){obj["$$$embind_global$$$"]=obj;var success=typeof $$$embind_global$$$==="object"&&obj["$$$embind_global$$$"]===obj;if(!success){delete obj["$$$embind_global$$$"]}return success}if(typeof $$$embind_global$$$==="object"){return $$$embind_global$$$}if(typeof global==="object"&&testGlobal(global)){$$$embind_global$$$=global}else if(typeof window==="object"&&testGlobal(window)){$$$embind_global$$$=window}if(typeof $$$embind_global$$$==="object"){return $$$embind_global$$$}throw Error("unable to get global object.")}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes,argWireTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var argN=new Array(argCount-1);var invokerFunction=function(handle,name,destructors,args){var offset=0;for(var i=0;i<argCount-1;++i){argN[i]=types[i+1].readValueFromPointer(args+offset);offset+=types[i+1].argPackAdvance}var rv=handle[name].apply(handle,argN);for(var i=0;i<argCount-1;++i){if(types[i+1].deleteObject){types[i+1].deleteObject(argN[i])}}if(!retType.isVoid){return retType.toWireType(destructors,rv)}};return __emval_addMethodCaller(invokerFunction)}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_new_object(){return __emval_register({})}function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_set_property(handle,key,value){handle=requireHandle(handle);key=requireHandle(key);value=requireHandle(value);handle[key]=value}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){Module["abort"]()}function _emscripten_get_heap_size(){return TOTAL_MEMORY}function abortOnCannotGrowMemory(requestedSize){abort("Cannot enlarge memory arrays to size "+requestedSize+" bytes. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function emscripten_realloc_buffer(size){var PAGE_MULTIPLE=65536;size=alignUp(size,PAGE_MULTIPLE);var old=Module["buffer"];var oldSize=old.byteLength;try{var result=wasmMemory.grow((size-oldSize)/65536);if(result!==(-1|0)){return Module["buffer"]=wasmMemory.buffer}else{return null}}catch(e){return null}}function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var LIMIT=2147483648-PAGE_MULTIPLE;if(requestedSize>LIMIT){return false}var MIN_TOTAL_MEMORY=16777216;var newSize=Math.max(oldSize,MIN_TOTAL_MEMORY);while(newSize<requestedSize){if(newSize<=536870912){newSize=alignUp(2*newSize,PAGE_MULTIPLE)}else{newSize=Math.min(alignUp((3*newSize+2147483648)/4,PAGE_MULTIPLE),LIMIT)}}var replacement=emscripten_realloc_buffer(newSize);if(!replacement||replacement.byteLength!=newSize){return false}updateGlobalBuffer(replacement);updateGlobalBufferViews();TOTAL_MEMORY=newSize;HEAPU32[DYNAMICTOP_PTR>>2]=requestedSize;return true}function _llvm_trap(){abort("trap!")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_emval();var ASSERTIONS=false;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){if(ASSERTIONS){assert(false,"Character code "+chr+" ("+String.fromCharCode(chr)+")  at offset "+i+" not in 0x00-0xFF.")}chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}var decodeBase64=typeof atob==="function"?atob:function(input){var keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");do{enc1=keyStr.indexOf(input.charAt(i++));enc2=keyStr.indexOf(input.charAt(i++));enc3=keyStr.indexOf(input.charAt(i++));enc4=keyStr.indexOf(input.charAt(i++));chr1=enc1<<2|enc2>>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!==64){output=output+String.fromCharCode(chr2)}if(enc4!==64){output=output+String.fromCharCode(chr3)}}while(i<input.length);return output};function intArrayFromBase64(s){try{var decoded=decodeBase64(s);var bytes=new Uint8Array(decoded.length);for(var i=0;i<decoded.length;++i){bytes[i]=decoded.charCodeAt(i)}return bytes}catch(_){throw new Error("Converting base64 string to bytes failed.")}}function tryParseAsDataURI(filename){if(!isDataURI(filename)){return}return intArrayFromBase64(filename.slice(dataURIPrefix.length))}function jsCall_ii(index,a1){return functionPointers[index](a1)}function jsCall_iii(index,a1,a2){return functionPointers[index](a1,a2)}function jsCall_iiii(index,a1,a2,a3){return functionPointers[index](a1,a2,a3)}function jsCall_iiiii(index,a1,a2,a3,a4){return functionPointers[index](a1,a2,a3,a4)}function jsCall_v(index){functionPointers[index]()}function jsCall_vi(index,a1){functionPointers[index](a1)}function jsCall_viiii(index,a1,a2,a3,a4){functionPointers[index](a1,a2,a3,a4)}function jsCall_viiiii(index,a1,a2,a3,a4,a5){functionPointers[index](a1,a2,a3,a4,a5)}function jsCall_viiiiii(index,a1,a2,a3,a4,a5,a6){functionPointers[index](a1,a2,a3,a4,a5,a6)}var asmGlobalArg={};var asmLibraryArg={"o":abort,"k":jsCall_ii,"j":jsCall_iii,"i":jsCall_iiii,"h":jsCall_iiiii,"g":jsCall_v,"f":jsCall_vi,"e":jsCall_viiii,"d":jsCall_viiiii,"c":jsCall_viiiiii,"m":___assert_fail,"N":___cxa_pure_virtual,"v":___setErrNo,"M":__embind_register_bool,"L":__embind_register_emval,"u":__embind_register_float,"n":__embind_register_integer,"l":__embind_register_memory_view,"t":__embind_register_std_string,"K":__embind_register_std_wstring,"J":__embind_register_void,"I":__emval_as,"H":__emval_call_void_method,"s":__emval_decref,"G":__emval_get_global,"F":__emval_get_method_caller,"E":__emval_get_property,"r":__emval_incref,"D":__emval_new_cstring,"C":__emval_new_object,"B":__emval_run_destructors,"p":__emval_set_property,"q":__emval_take_value,"b":_abort,"A":_emscripten_get_heap_size,"z":_emscripten_memcpy_big,"y":_emscripten_resize_heap,"x":_llvm_trap,"w":abortOnCannotGrowMemory,"a":DYNAMICTOP_PTR};var asm=Module["asm"](asmGlobalArg,asmLibraryArg,buffer);Module["asm"]=asm;var __GLOBAL__sub_I_bind_cpp=Module["__GLOBAL__sub_I_bind_cpp"]=function(){return Module["asm"]["O"].apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return Module["asm"]["P"].apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return Module["asm"]["Q"].apply(null,arguments)};var _free=Module["_free"]=function(){return Module["asm"]["R"].apply(null,arguments)};var _image=Module["_image"]=function(){return Module["asm"]["S"].apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return Module["asm"]["T"].apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return Module["asm"]["W"].apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return Module["asm"]["X"].apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return Module["asm"]["Y"].apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return Module["asm"]["U"].apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return Module["asm"]["V"].apply(null,arguments)};Module["asm"]=asm;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run();
diff --git a/ui/file_manager/image_loader/piex/tests.html b/ui/file_manager/image_loader/piex/tests.html
index 62b00b6..9d5a8ee 100644
--- a/ui/file_manager/image_loader/piex/tests.html
+++ b/ui/file_manager/image_loader/piex/tests.html
@@ -229,7 +229,7 @@
   window.onload = function loadPiexModule() {
     let script = document.createElement('script');
     document.head.appendChild(script);
-    script.src = '/a.out.js';
+    script.src = '/piex.wasm';
   };
 
   window.onerror = (error) => {
diff --git a/ui/file_manager/integration_tests/file_manager/providers.js b/ui/file_manager/integration_tests/file_manager/providers.js
index 0c15e2f..c9a49e7 100644
--- a/ui/file_manager/integration_tests/file_manager/providers.js
+++ b/ui/file_manager/integration_tests/file_manager/providers.js
@@ -275,4 +275,32 @@
     // ejected.
     return IGNORE_APP_ERRORS;
   };
+
+  /**
+   * Tests that when online, the install new service button is enabled.
+   */
+  testcase.installNewServiceOnline = async () => {
+    const appId = await setUpProvider('manifest.json');
+    await showProvidersMenu(appId);
+
+    const selector = '#add-new-services-menu:not([hidden]) ' +
+        'cr-menu-item[command="#install-new-extension"]:not([disabled])';
+    const element = await remoteCall.waitForElement(appId, selector);
+    chrome.test.assertEq('Install new service', element.text);
+    chrome.test.assertFalse(element.hidden);
+  };
+
+  /**
+   * Tests that when offline, the install new service button is disabled.
+   */
+  testcase.installNewServiceOffline = async () => {
+    const appId = await setUpProvider('manifest.json');
+    await showProvidersMenu(appId);
+
+    const selector = '#add-new-services-menu:not([hidden]) ' +
+        'cr-menu-item[command="#install-new-extension"][disabled]';
+    const element = await remoteCall.waitForElement(appId, selector);
+    chrome.test.assertEq('Install new service', element.text);
+    chrome.test.assertFalse(element.hidden);
+  };
 })();
diff --git a/ui/file_manager/video_player/css/video_player.css b/ui/file_manager/video_player/css/video_player.css
index 5abcbbe..de18f48 100644
--- a/ui/file_manager/video_player/css/video_player.css
+++ b/ui/file_manager/video_player/css/video_player.css
@@ -67,6 +67,13 @@
 video {
   height: 100%;
   left: 0;
+
+  /*
+   * Since r614513, <video> elements take focus on click which causes yellow
+   * lines to appear unless the outline is disabled. See crbug/917503.
+   */
+  outline: none;
+
   pointer-events: none;
   position: absolute;
   top: 0;
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index c59a02b..ad85247 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -288,15 +288,16 @@
   time_of_last_blur_ = base::Time::UnixEpoch();
   UpdateInputMethodObserver();
 
-  for (KeyboardControllerObserver& observer : observer_list_)
-    observer.OnKeyboardEnabledChanged(true);
-
   ActivateKeyboardInContainer(
       layout_delegate_->GetContainerForDefaultDisplay());
 
   // Start preloading the virtual keyboard UI in the background, so that it
   // shows up faster when needed.
   LoadKeyboardWindowInBackground();
+
+  // Notify observers after the keyboard window has a root window.
+  for (KeyboardControllerObserver& observer : observer_list_)
+    observer.OnKeyboardEnabledChanged(true);
 }
 
 void KeyboardController::DisableKeyboard() {
@@ -323,10 +324,12 @@
   animation_observer_.reset();
 
   ime_observer_.RemoveAll();
-  for (KeyboardControllerObserver& observer : observer_list_)
-    observer.OnKeyboardEnabledChanged(false);
   ui_->SetController(nullptr);
   ui_.reset();
+
+  // Notify observers after |ui_| is reset so that IsEnabled() is false.
+  for (KeyboardControllerObserver& observer : observer_list_)
+    observer.OnKeyboardEnabledChanged(false);
 }
 
 void KeyboardController::ActivateKeyboardInContainer(aura::Window* parent) {
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 4df6483..e411155 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -772,4 +772,55 @@
   EXPECT_TRUE(IsKeyboardDisabled());
 }
 
+class MockKeyboardControllerObserver : public KeyboardControllerObserver {
+ public:
+  MockKeyboardControllerObserver() = default;
+  ~MockKeyboardControllerObserver() override = default;
+
+  // KeyboardControllerObserver:
+  MOCK_METHOD1(OnKeyboardEnabledChanged, void(bool is_enabled));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockKeyboardControllerObserver);
+};
+
+TEST_F(KeyboardControllerTest, OnKeyboardEnabledChangedToEnabled) {
+  // Start with the keyboard disabled.
+  keyboard::SetTouchKeyboardEnabled(false);
+
+  MockKeyboardControllerObserver mock_observer;
+  controller().AddObserver(&mock_observer);
+
+  EXPECT_CALL(mock_observer, OnKeyboardEnabledChanged(true))
+      .WillOnce(testing::InvokeWithoutArgs([]() {
+        auto* controller = keyboard::KeyboardController::Get();
+        ASSERT_TRUE(controller);
+        EXPECT_TRUE(controller->IsEnabled());
+        EXPECT_TRUE(controller->GetKeyboardWindow());
+        EXPECT_TRUE(controller->GetRootWindow());
+      }));
+
+  keyboard::SetTouchKeyboardEnabled(true);
+
+  controller().RemoveObserver(&mock_observer);
+}
+
+TEST_F(KeyboardControllerTest, OnKeyboardEnabledChangedToDisabled) {
+  MockKeyboardControllerObserver mock_observer;
+  controller().AddObserver(&mock_observer);
+
+  EXPECT_CALL(mock_observer, OnKeyboardEnabledChanged(false))
+      .WillOnce(testing::InvokeWithoutArgs([]() {
+        auto* controller = keyboard::KeyboardController::Get();
+        ASSERT_TRUE(controller);
+        EXPECT_FALSE(controller->IsEnabled());
+        EXPECT_FALSE(controller->GetKeyboardWindow());
+        EXPECT_FALSE(controller->GetRootWindow());
+      }));
+
+  keyboard::SetTouchKeyboardEnabled(false);
+
+  controller().RemoveObserver(&mock_observer);
+}
+
 }  // namespace keyboard
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index bc651e1..1e5aa6f 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -32,6 +32,8 @@
     "gpu/wayland_surface_factory.h",
     "host/wayland_buffer_manager.cc",
     "host/wayland_buffer_manager.h",
+    "host/wayland_clipboard.cc",
+    "host/wayland_clipboard.h",
     "host/wayland_connection.cc",
     "host/wayland_connection.h",
     "host/wayland_connection_connector.cc",
@@ -127,7 +129,7 @@
   ]
 
   if (is_linux && !is_chromeos) {
-    deps += ["//ui/base/ime/linux"]
+    deps += [ "//ui/base/ime/linux" ]
   }
 
   defines = [ "OZONE_IMPLEMENTATION" ]
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
new file mode 100644
index 0000000..ae4fb06
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+
+namespace ui {
+
+WaylandClipboard::WaylandClipboard(
+    WaylandDataDeviceManager* data_device_manager,
+    WaylandDataDevice* data_device)
+    : data_device_manager_(data_device_manager), data_device_(data_device) {
+  DCHECK(data_device_manager_);
+  DCHECK(data_device_);
+}
+
+WaylandClipboard::~WaylandClipboard() = default;
+
+void WaylandClipboard::OfferClipboardData(
+    const PlatformClipboard::DataMap& data_map,
+    PlatformClipboard::OfferDataClosure callback) {
+  if (!clipboard_data_source_) {
+    clipboard_data_source_ = data_device_manager_->CreateSource();
+    clipboard_data_source_->WriteToClipboard(data_map);
+  }
+  clipboard_data_source_->UpdateDataMap(data_map);
+  std::move(callback).Run();
+}
+
+void WaylandClipboard::RequestClipboardData(
+    const std::string& mime_type,
+    PlatformClipboard::DataMap* data_map,
+    PlatformClipboard::RequestDataClosure callback) {
+  read_clipboard_closure_ = std::move(callback);
+
+  DCHECK(data_map);
+  data_map_ = data_map;
+  if (!data_device_->RequestSelectionData(mime_type))
+    SetData({}, mime_type);
+}
+
+bool WaylandClipboard::IsSelectionOwner() {
+  return !!clipboard_data_source_;
+}
+
+void WaylandClipboard::SetSequenceNumberUpdateCb(
+    PlatformClipboard::SequenceNumberUpdateCb cb) {
+  CHECK(update_sequence_cb_.is_null())
+      << " The callback can be installed only once.";
+  update_sequence_cb_ = std::move(cb);
+}
+
+void WaylandClipboard::GetAvailableMimeTypes(
+    PlatformClipboard::GetMimeTypesClosure callback) {
+  std::move(callback).Run(data_device_->GetAvailableMimeTypes());
+}
+
+void WaylandClipboard::DataSourceCancelled() {
+  DCHECK(clipboard_data_source_);
+  SetData({}, {});
+  clipboard_data_source_.reset();
+}
+
+void WaylandClipboard::SetData(const std::string& contents,
+                               const std::string& mime_type) {
+  if (!data_map_)
+    return;
+
+  (*data_map_)[mime_type] =
+      std::vector<uint8_t>(contents.begin(), contents.end());
+
+  if (!read_clipboard_closure_.is_null()) {
+    auto it = data_map_->find(mime_type);
+    DCHECK(it != data_map_->end());
+    std::move(read_clipboard_closure_).Run(it->second);
+  }
+  data_map_ = nullptr;
+}
+
+void WaylandClipboard::UpdateSequenceNumber() {
+  if (!update_sequence_cb_.is_null())
+    update_sequence_cb_.Run();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.h b/ui/ozone/platform/wayland/host/wayland_clipboard.h
new file mode 100644
index 0000000..61daf6a
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
+
+#include "base/callback.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/public/platform_clipboard.h"
+
+namespace ui {
+
+class WaylandDataDevice;
+class WaylandDataDeviceManager;
+
+// Handles clipboard operations.
+//
+// Owned by WaylandConnection, which provides a data device and a data device
+// manager.
+class WaylandClipboard : public PlatformClipboard {
+ public:
+  WaylandClipboard(WaylandDataDeviceManager* data_device_manager,
+                   WaylandDataDevice* data_device);
+  virtual ~WaylandClipboard();
+
+  // PlatformClipboard.
+  void OfferClipboardData(
+      const PlatformClipboard::DataMap& data_map,
+      PlatformClipboard::OfferDataClosure callback) override;
+  void RequestClipboardData(
+      const std::string& mime_type,
+      PlatformClipboard::DataMap* data_map,
+      PlatformClipboard::RequestDataClosure callback) override;
+  void GetAvailableMimeTypes(
+      PlatformClipboard::GetMimeTypesClosure callback) override;
+  bool IsSelectionOwner() override;
+  void SetSequenceNumberUpdateCb(
+      PlatformClipboard::SequenceNumberUpdateCb cb) override;
+
+  void DataSourceCancelled();
+  void SetData(const std::string& contents, const std::string& mime_type);
+  void UpdateSequenceNumber();
+
+ private:
+  // Holds a temporary instance of the client's clipboard content
+  // so that we can asynchronously write to it.
+  PlatformClipboard::DataMap* data_map_ = nullptr;
+
+  // Notifies whenever clipboard sequence number is changed. Can be empty if not
+  // set.
+  PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_;
+
+  // Stores the callback to be invoked upon data reading from clipboard.
+  PlatformClipboard::RequestDataClosure read_clipboard_closure_;
+
+  std::unique_ptr<WaylandDataSource> clipboard_data_source_;
+
+  // These two instances are owned by the connection.
+  WaylandDataDeviceManager* const data_device_manager_ = nullptr;
+  WaylandDataDevice* const data_device_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandClipboard);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index 0be2a976..0c72f2d 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -265,44 +265,6 @@
   client_associated_ptr_->OnPresentation(widget, buffer_id, feedback);
 }
 
-PlatformClipboard* WaylandConnection::GetPlatformClipboard() {
-  return this;
-}
-
-void WaylandConnection::OfferClipboardData(
-    const PlatformClipboard::DataMap& data_map,
-    PlatformClipboard::OfferDataClosure callback) {
-  if (!clipboard_data_source_) {
-    clipboard_data_source_ = data_device_manager_->CreateSource();
-    clipboard_data_source_->WriteToClipboard(data_map);
-  }
-  clipboard_data_source_->UpdateDataMap(data_map);
-  std::move(callback).Run();
-}
-
-void WaylandConnection::RequestClipboardData(
-    const std::string& mime_type,
-    PlatformClipboard::DataMap* data_map,
-    PlatformClipboard::RequestDataClosure callback) {
-  read_clipboard_closure_ = std::move(callback);
-
-  DCHECK(data_map);
-  data_map_ = data_map;
-  if (!data_device_->RequestSelectionData(mime_type))
-    SetClipboardData({}, mime_type);
-}
-
-bool WaylandConnection::IsSelectionOwner() {
-  return !!clipboard_data_source_;
-}
-
-void WaylandConnection::SetSequenceNumberUpdateCb(
-    PlatformClipboard::SequenceNumberUpdateCb cb) {
-  CHECK(update_sequence_cb_.is_null())
-      << " The callback can be installed only once.";
-  update_sequence_cb_ = std::move(cb);
-}
-
 ozone::mojom::WaylandConnectionPtr WaylandConnection::BindInterface() {
   DCHECK(!binding_.is_bound());
   ozone::mojom::WaylandConnectionPtr ptr;
@@ -365,38 +327,6 @@
     pointer_->ResetFlags();
 }
 
-void WaylandConnection::GetAvailableMimeTypes(
-    PlatformClipboard::GetMimeTypesClosure callback) {
-  std::move(callback).Run(data_device_->GetAvailableMimeTypes());
-}
-
-void WaylandConnection::DataSourceCancelled() {
-  DCHECK(clipboard_data_source_);
-  SetClipboardData({}, {});
-  clipboard_data_source_.reset();
-}
-
-void WaylandConnection::SetClipboardData(const std::string& contents,
-                                         const std::string& mime_type) {
-  if (!data_map_)
-    return;
-
-  (*data_map_)[mime_type] =
-      std::vector<uint8_t>(contents.begin(), contents.end());
-
-  if (!read_clipboard_closure_.is_null()) {
-    auto it = data_map_->find(mime_type);
-    DCHECK(it != data_map_->end());
-    std::move(read_clipboard_closure_).Run(it->second);
-  }
-  data_map_ = nullptr;
-}
-
-void WaylandConnection::UpdateClipboardSequenceNumber() {
-  if (!update_sequence_cb_.is_null())
-    update_sequence_cb_.Run();
-}
-
 void WaylandConnection::OnDispatcherListChanged() {
   StartProcessingEvents();
 }
@@ -429,6 +359,8 @@
   DCHECK(!data_device_);
   wl_data_device* data_device = data_device_manager_->GetDevice();
   data_device_ = std::make_unique<WaylandDataDevice>(this, data_device);
+  clipboard_ = std::make_unique<WaylandClipboard>(data_device_manager_.get(),
+                                                  data_device_.get());
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index 2edbdb7..e38f5e1 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -17,6 +17,7 @@
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
 #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
 #include "ui/ozone/platform/wayland/host/wayland_data_device.h"
 #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
@@ -26,7 +27,6 @@
 #include "ui/ozone/platform/wayland/host/wayland_pointer.h"
 #include "ui/ozone/platform/wayland/host/wayland_touch.h"
 #include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
-#include "ui/ozone/public/platform_clipboard.h"
 
 namespace ui {
 
@@ -36,9 +36,7 @@
 class WaylandWindow;
 class WaylandZwpLinuxDmabuf;
 
-// TODO(crbug.com/942203): factor out PlatformClipboard to a separate class.
 class WaylandConnection : public PlatformEventSource,
-                          public PlatformClipboard,
                           public ozone::mojom::WaylandConnection,
                           public base::MessagePumpLibevent::FdWatcher {
  public:
@@ -134,6 +132,8 @@
   // Returns the current pointer, which may be null.
   WaylandPointer* pointer() const { return pointer_.get(); }
 
+  WaylandClipboard* clipboard() const { return clipboard_.get(); }
+
   WaylandDataSource* drag_data_source() const {
     return dragdrop_data_source_.get();
   }
@@ -151,27 +151,6 @@
 
   WaylandZwpLinuxDmabuf* zwp_dmabuf() const { return zwp_dmabuf_.get(); }
 
-  // Clipboard implementation.
-  PlatformClipboard* GetPlatformClipboard();
-  void DataSourceCancelled();
-  void SetClipboardData(const std::string& contents,
-                        const std::string& mime_type);
-  void UpdateClipboardSequenceNumber();
-
-  // PlatformClipboard.
-  void OfferClipboardData(
-      const PlatformClipboard::DataMap& data_map,
-      PlatformClipboard::OfferDataClosure callback) override;
-  void RequestClipboardData(
-      const std::string& mime_type,
-      PlatformClipboard::DataMap* data_map,
-      PlatformClipboard::RequestDataClosure callback) override;
-  void GetAvailableMimeTypes(
-      PlatformClipboard::GetMimeTypesClosure callback) override;
-  bool IsSelectionOwner() override;
-  void SetSequenceNumberUpdateCb(
-      PlatformClipboard::SequenceNumberUpdateCb cb) override;
-
   // Returns bound pointer to own mojo interface.
   ozone::mojom::WaylandConnectionPtr BindInterface();
 
@@ -265,7 +244,7 @@
 
   std::unique_ptr<WaylandDataDeviceManager> data_device_manager_;
   std::unique_ptr<WaylandDataDevice> data_device_;
-  std::unique_ptr<WaylandDataSource> clipboard_data_source_;
+  std::unique_ptr<WaylandClipboard> clipboard_;
   std::unique_ptr<WaylandDataSource> dragdrop_data_source_;
   std::unique_ptr<WaylandKeyboard> keyboard_;
   std::unique_ptr<WaylandOutputManager> wayland_output_manager_;
@@ -284,17 +263,6 @@
 
   uint32_t serial_ = 0;
 
-  // Holds a temporary instance of the client's clipboard content
-  // so that we can asynchronously write to it.
-  PlatformClipboard::DataMap* data_map_ = nullptr;
-
-  // Notifies whenever clipboard sequence number is changed. Can be empty if not
-  // set.
-  PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_;
-
-  // Stores the callback to be invoked upon data reading from clipboard.
-  RequestDataClosure read_clipboard_closure_;
-
   ozone::mojom::WaylandConnectionClientAssociatedPtr client_associated_ptr_;
   mojo::Binding<ozone::mojom::WaylandConnection> binding_;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.cc b/ui/ozone/platform/wayland/host/wayland_data_device.cc
index a8e16cd..f258e8d 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_device.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -175,7 +175,7 @@
                                                 const std::string& mime_type) {
   std::string contents;
   ReadDataFromFD(std::move(fd), &contents);
-  connection_->SetClipboardData(contents, mime_type);
+  connection_->clipboard()->SetData(contents, mime_type);
 }
 
 void WaylandDataDevice::ReadDragDataFromFD(
@@ -208,7 +208,7 @@
                                     wl_data_offer* offer) {
   auto* self = static_cast<WaylandDataDevice*>(data);
 
-  self->connection_->UpdateClipboardSequenceNumber();
+  self->connection_->clipboard()->UpdateSequenceNumber();
 
   DCHECK(!self->new_offer_);
   self->new_offer_.reset(new WaylandDataOffer(offer));
@@ -335,7 +335,7 @@
     self->selection_offer_.reset();
 
     // Clear Clipboard cache.
-    self->connection_->SetClipboardData(std::string(), std::string());
+    self->connection_->clipboard()->SetData(std::string(), std::string());
     return;
   }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
index 406ca31..d5b25bb 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
@@ -35,7 +35,7 @@
     DCHECK(connection);
     // See comment above for reasoning to access the WaylandConnection
     // directly from here.
-    delegate_ = connection->GetPlatformClipboard();
+    delegate_ = connection->clipboard();
 
     DCHECK(delegate_);
   }
diff --git a/ui/ozone/platform/wayland/host/wayland_data_source.cc b/ui/ozone/platform/wayland/host/wayland_data_source.cc
index 592b51f..f8ba38a0 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -112,7 +112,7 @@
     self->connection_->FinishDragSession(self->dnd_action_,
                                          self->source_window_);
   } else {
-    self->connection_->DataSourceCancelled();
+    self->connection_->clipboard()->DataSourceCancelled();
   }
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
index 08d5dbf8b..8e99460 100644
--- a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
@@ -12,6 +12,10 @@
 
 namespace ui {
 
+namespace {
+constexpr uint32_t kImmedVerstion = 3;
+}
+
 WaylandZwpLinuxDmabuf::WaylandZwpLinuxDmabuf(
     zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
     WaylandConnection* connection)
@@ -45,10 +49,6 @@
   struct zwp_linux_buffer_params_v1* params =
       zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get());
 
-  // Store the |params| with the corresponding |callback| to identify newly
-  // created buffer and notify the client about it via the |callback|.
-  pending_params_.insert(std::make_pair(params, std::move(callback)));
-
   base::ScopedFD fd(file.TakePlatformFile());
 
   for (size_t i = 0; i < planes_count; i++) {
@@ -65,10 +65,23 @@
                                    offsets[i], strides[i], modifier_hi,
                                    modifier_lo);
   }
-  zwp_linux_buffer_params_v1_add_listener(params, &params_listener, this);
-  zwp_linux_buffer_params_v1_create(params, size.width(), size.height(), format,
-                                    0);
 
+  // It's possible to avoid waiting until the buffer is created and have it
+  // immediately. This method is only available since the protocol version 3.
+  if (zwp_linux_dmabuf_v1_get_version(zwp_linux_dmabuf_.get()) >=
+      kImmedVerstion) {
+    wl::Object<wl_buffer> buffer(zwp_linux_buffer_params_v1_create_immed(
+        params, size.width(), size.height(), format, 0));
+    std::move(callback).Run(std::move(buffer));
+  } else {
+    // Store the |params| with the corresponding |callback| to identify newly
+    // created buffer and notify the client about it via the |callback|.
+    pending_params_.insert(std::make_pair(params, std::move(callback)));
+
+    zwp_linux_buffer_params_v1_add_listener(params, &params_listener, this);
+    zwp_linux_buffer_params_v1_create(params, size.width(), size.height(),
+                                      format, 0);
+  }
   connection_->ScheduleFlush();
 }
 
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 55a51f9..d4f26d8b 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -126,7 +126,7 @@
 
   PlatformClipboard* GetPlatformClipboard() override {
     DCHECK(connection_);
-    return connection_->GetPlatformClipboard();
+    return connection_->clipboard();
   }
 
   bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index 34aeec3..da67163 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -125,7 +125,9 @@
 }
 
 gfx::Point DragDropClientMac::LocationInView(NSPoint point) const {
-  return gfx::Point(point.x, NSHeight([bridge_->ns_window() frame]) - point.y);
+  NSRect content_rect = [bridge_->ns_window()
+      contentRectForFrameRect:[bridge_->ns_window() frame]];
+  return gfx::Point(point.x, NSHeight(content_rect) - point.y);
 }
 
 }  // namespace views