diff --git a/DEPS b/DEPS
index 95500fb9..844c40ed 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c94ff43cf846cf470b7d46e1d2aaa1724c5cc948',
+  'skia_revision': '69fd008199989c5a5a96f992dcaa4089b63f490f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '31d706bd8122830a15c574ba461e8f1a41427b9c',
+  'pdfium_revision': '073797b6cff9947070077ff4b3cccc63059dc4e5',
   # 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.
@@ -96,7 +96,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': '9d0d9fc4552beb4f7d5eefb4b2ecde33f58bd421',
+  'catapult_revision': '1a1b45a572561a9eff29a35c9b59e2d0ff55bd4d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5c99a19..dadcf80 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -862,6 +862,8 @@
     "threading/platform_thread_win.cc",
     "threading/post_task_and_reply_impl.cc",
     "threading/post_task_and_reply_impl.h",
+    "threading/scoped_may_block.cc",
+    "threading/scoped_may_block.h",
     "threading/sequence_local_storage_map.cc",
     "threading/sequence_local_storage_map.h",
     "threading/sequence_local_storage_slot.cc",
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 1d69ad7..59fdc2b 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -269,6 +269,7 @@
 }
 
 #elif defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_IOS) && \
+    !defined(OS_FUCHSIA) &&                                         \
     (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY))
 
 int g_child_crash_pipe;
diff --git a/base/nix/xdg_util.cc b/base/nix/xdg_util.cc
index 41a8914..8348b06c 100644
--- a/base/nix/xdg_util.cc
+++ b/base/nix/xdg_util.cc
@@ -59,6 +59,8 @@
   if (env->GetVar("XDG_CURRENT_DESKTOP", &xdg_current_desktop)) {
     // Not all desktop environments set this env var as of this writing.
     if (base::StartsWith(xdg_current_desktop, "Unity",
+                         base::CompareCase::SENSITIVE) ||
+        base::StartsWith(xdg_current_desktop, "Pantheon",
                          base::CompareCase::SENSITIVE)) {
       // gnome-fallback sessions set XDG_CURRENT_DESKTOP to Unity
       // DESKTOP_SESSION can be gnome-fallback or gnome-fallback-compiz
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index 28a50d3..8d4c520 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -373,16 +373,29 @@
     fflush(stdout);
 
     // We do not have reliable details about test results (parsing test
-    // stdout is known to be unreliable), apply the executable exit code
-    // to all tests.
-    // TODO(phajdan.jr): Be smarter about this, e.g. retry each test
-    // individually.
-    for (size_t i = 0; i < test_names.size(); i++) {
+    // stdout is known to be unreliable).
+    if (test_names.size() == 1) {
+      // There is only one test. Try to determine status by exit code.
+      const std::string& test_name = test_names.front();
       TestResult test_result;
-      test_result.full_name = test_names[i];
-      test_result.status = TestResult::TEST_UNKNOWN;
+      test_result.full_name = test_name;
+
+      if (was_timeout) {
+        test_result.status = TestResult::TEST_TIMEOUT;
+      } else if (exit_code != 0) {
+        test_result.status = TestResult::TEST_FAILURE;
+      } else {
+        // It's strange case when test executed successfully,
+        // but we failed to read machine-readable report for it.
+        test_result.status = TestResult::TEST_UNKNOWN;
+      }
+
       test_launcher->OnTestFinished(test_result);
       called_any_callback = true;
+    } else {
+      // There is more than one test. Retry them individually.
+      for (const std::string& test_name : test_names)
+        tests_to_relaunch->push_back(test_name);
     }
   }
 
diff --git a/base/threading/scoped_may_block.cc b/base/threading/scoped_may_block.cc
new file mode 100644
index 0000000..7533745
--- /dev/null
+++ b/base/threading/scoped_may_block.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/scoped_may_block.h"
+
+#include "base/lazy_instance.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
+    tls_blocking_observer = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+namespace internal {
+
+void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) {
+  DCHECK(!tls_blocking_observer.Get().Get());
+  tls_blocking_observer.Get().Set(blocking_observer);
+}
+
+}  // namespace internal
+
+ScopedMayBlock::ScopedMayBlock() {
+  internal::BlockingObserver* blocking_observer =
+      tls_blocking_observer.Get().Get();
+  if (blocking_observer)
+    blocking_observer->BlockingScopeEntered();
+}
+
+ScopedMayBlock::~ScopedMayBlock() {
+  internal::BlockingObserver* blocking_observer =
+      tls_blocking_observer.Get().Get();
+  if (blocking_observer)
+    blocking_observer->BlockingScopeExited();
+}
+
+}  // namespace base
diff --git a/base/threading/scoped_may_block.h b/base/threading/scoped_may_block.h
new file mode 100644
index 0000000..284a11c
--- /dev/null
+++ b/base/threading/scoped_may_block.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SCOPED_MAY_BLOCK_H
+#define BASE_THREADING_SCOPED_MAY_BLOCK_H
+
+#include "base/base_export.h"
+
+namespace base {
+
+// This class should be instantiated in every scope where a blocking call is
+// made.
+//
+// Instantiation will hint the BlockingObserver for this thread about the
+// scope of the blocking operation. In particular, on TaskScheduler owned
+// threads, this will allow the thread to be replaced in its pool if the
+// blocking scope doesn't expire shortly.
+class BASE_EXPORT ScopedMayBlock {
+ public:
+  ScopedMayBlock();
+  ~ScopedMayBlock();
+};
+
+namespace internal {
+
+// Interface for an observer to be informed when a thread enters or exits
+// the scope of a ScopedMayBlock object.
+class BASE_EXPORT BlockingObserver {
+ public:
+  virtual void BlockingScopeEntered() = 0;
+  virtual void BlockingScopeExited() = 0;
+};
+
+void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer);
+
+}  // namespace internal
+
+}  // namespace base
+
+#endif  // BASE_THREADING_SCOPED_MAY_BLOCK_H
diff --git a/cc/base/switches.cc b/cc/base/switches.cc
index d9a1c5b..a720f11 100644
--- a/cc/base/switches.cc
+++ b/cc/base/switches.cc
@@ -62,13 +62,6 @@
 const char kCompositedSurfaceBorders[] = "surface";
 const char kCompositedLayerBorders[] = "layer";
 
-// TODO(dcastagna): Draw debug quad borders only when it is actually
-// an overlay candidate.
-// Renders a border around GL composited overlay candidate quads to
-// help debug and study overlay support.
-const char kGlCompositedOverlayCandidateQuadBorder[] =
-    "gl-composited-overlay-candidate-quad-border";
-
 // Draws a heads-up-display showing Frames Per Second as well as GPU memory
 // usage. If you also use --enable-logging=stderr --vmodule="head*=1" then FPS
 // will also be output to the console log.
@@ -97,18 +90,6 @@
 const char kEnableLayerLists[] = "enable-layer-lists";
 const char kUIEnableLayerLists[] = "ui-enable-layer-lists";
 
-// Visualize overdraw by color-coding elements based on if they have other
-// elements drawn underneath. This is good for showing where the UI might be
-// doing more rendering work than necessary. The colors are hinting at the
-// amount of overdraw on your screen for each pixel, as follows:
-//
-// True color: No overdraw.
-// Blue: Overdrawn once.
-// Green: Overdrawn twice.
-// Pink: Overdrawn three times.
-// Red: Overdrawn four or more times.
-const char kShowOverdrawFeedback[] = "show-overdraw-feedback";
-
 // Prevents the layer tree unit tests from timing out.
 const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
 
@@ -118,10 +99,5 @@
 // Makes pixel tests write their output instead of read it.
 const char kCCRebaselinePixeltests[] = "cc-rebaseline-pixeltests";
 
-// Disable re-use of non-exact resources to fulfill ResourcePool requests.
-// Intended only for use in layout or pixel tests to reduce noise.
-const char kDisallowNonExactResourceReuse[] =
-    "disallow-non-exact-resource-reuse";
-
 }  // namespace switches
 }  // namespace cc
diff --git a/cc/base/switches.h b/cc/base/switches.h
index 5320fd1..420cc8b 100644
--- a/cc/base/switches.h
+++ b/cc/base/switches.h
@@ -35,7 +35,6 @@
 // Debug visualizations.
 CC_BASE_EXPORT extern const char kShowCompositedLayerBorders[];
 CC_BASE_EXPORT extern const char kUIShowCompositedLayerBorders[];
-CC_BASE_EXPORT extern const char kGlCompositedOverlayCandidateQuadBorder[];
 CC_BASE_EXPORT extern const char kShowFPSCounter[];
 CC_BASE_EXPORT extern const char kUIShowFPSCounter[];
 CC_BASE_EXPORT extern const char kShowLayerAnimationBounds[];
@@ -48,7 +47,6 @@
 CC_BASE_EXPORT extern const char kUIShowScreenSpaceRects[];
 CC_BASE_EXPORT extern const char kEnableLayerLists[];
 CC_BASE_EXPORT extern const char kUIEnableLayerLists[];
-CC_BASE_EXPORT extern const char kShowOverdrawFeedback[];
 CC_BASE_EXPORT extern const char kCompositedRenderPassBorders[];
 CC_BASE_EXPORT extern const char kCompositedSurfaceBorders[];
 CC_BASE_EXPORT extern const char kCompositedLayerBorders[];
@@ -57,7 +55,6 @@
 CC_BASE_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
 CC_BASE_EXPORT extern const char kCCLayerTreeTestLongTimeout[];
 CC_BASE_EXPORT extern const char kCCRebaselinePixeltests[];
-CC_BASE_EXPORT extern const char kDisallowNonExactResourceReuse[];
 
 }  // namespace switches
 }  // namespace cc
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
index 9f95186..c9b6104 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
@@ -288,14 +288,10 @@
         if (window.hasPermission(permission.WRITE_EXTERNAL_STORAGE)) {
             onDownloadStartNoStream(downloadInfo);
         } else if (window.canRequestPermission(permission.WRITE_EXTERNAL_STORAGE)) {
-            PermissionCallback permissionCallback = new PermissionCallback() {
-                @Override
-                public void onRequestPermissionsResult(
-                        String[] permissions, int[] grantResults) {
-                    if (grantResults.length > 0
-                            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                        onDownloadStartNoStream(downloadInfo);
-                    }
+            PermissionCallback permissionCallback = (permissions, grantResults) -> {
+                if (grantResults.length > 0
+                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    onDownloadStartNoStream(downloadInfo);
                 }
             };
             window.requestPermissions(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
index 6f5a192d..3084dba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
@@ -149,34 +149,22 @@
         TextView dialogText = (TextView) view.findViewById(R.id.text);
         dialogText.setText(R.string.missing_storage_permission_download_education_text);
 
-        final PermissionCallback permissionCallback = new PermissionCallback() {
-            @Override
-            public void onRequestPermissionsResult(String[] permissions, int[] grantResults) {
-                nativeOnAcquirePermissionResult(callbackId,
+        final PermissionCallback permissionCallback =
+                (permissions, grantResults) -> nativeOnAcquirePermissionResult(callbackId,
                         grantResults.length > 0
                                 && grantResults[0] == PackageManager.PERMISSION_GRANTED,
                         null);
-            }
-        };
 
         AlertDialog.Builder builder =
                 new AlertDialog.Builder(activity, R.style.AlertDialogTheme)
                         .setView(view)
                         .setPositiveButton(R.string.infobar_update_permissions_button_text,
-                                new DialogInterface.OnClickListener() {
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int id) {
-                                        windowAndroid.requestPermissions(
-                                                new String[] {permission.WRITE_EXTERNAL_STORAGE},
-                                                permissionCallback);
-                                    }
-                                })
-                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
-                            @Override
-                            public void onCancel(DialogInterface dialog) {
-                                nativeOnAcquirePermissionResult(callbackId, false, null);
-                            }
-                        });
+                                (DialogInterface.OnClickListener) (dialog, id) -> windowAndroid
+                                        .requestPermissions(
+                                        new String[]{permission.WRITE_EXTERNAL_STORAGE},
+                                        permissionCallback))
+                        .setOnCancelListener(
+                                dialog -> nativeOnAcquirePermissionResult(callbackId, false, null));
         builder.create().show();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index b1defb22..2fd9daf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -245,12 +245,7 @@
     protected void init() {
         DownloadController.setDownloadNotificationService(this);
         // Post a delayed task to resume all pending downloads.
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mDownloadNotifier.resumePendingDownloads();
-            }
-        }, RESUME_DELAY_MILLIS);
+        mHandler.postDelayed(() -> mDownloadNotifier.resumePendingDownloads(), RESUME_DELAY_MILLIS);
         parseUMAStatsEntriesFromSharedPrefs();
         Iterator<DownloadUmaStatsEntry> iterator = mUmaEntries.iterator();
         boolean hasChanges = false;
@@ -354,13 +349,9 @@
         removeAutoResumableDownload(item.getId());
         // Post a delayed task to avoid an issue that when connectivity status just changed
         // to CONNECTED, immediately establishing a connection will sometimes fail.
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                resumeDownload(LegacyHelpers.buildLegacyContentId(false, item.getId()),
-                        item, false);
-            }
-        }, mUpdateDelayInMillis);
+        mHandler.postDelayed(
+                () -> resumeDownload(LegacyHelpers.buildLegacyContentId(false, item.getId()),
+                        item, false), mUpdateDelayInMillis);
     }
 
     /**
@@ -578,12 +569,9 @@
         }
         updateAllNotifications(progressPendingUpdate);
 
-        Runnable scheduleNextUpdateTask = new Runnable(){
-            @Override
-            public void run() {
-                mIsUIUpdateScheduled = false;
-                scheduleUpdateIfNeeded();
-            }
+        Runnable scheduleNextUpdateTask = () -> {
+            mIsUIUpdateScheduled = false;
+            scheduleUpdateIfNeeded();
         };
         mHandler.postDelayed(scheduleNextUpdateTask, mUpdateDelayInMillis);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
index 624d0d8..1211b02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
@@ -50,13 +50,10 @@
         // own ActivityStateListener. If multiple tabs are showing the downloads page, multiple
         // requests to check for externally removed downloads will be issued when the activity is
         // resumed.
-        mActivityStateListener = new ActivityStateListener() {
-            @Override
-            public void onActivityStateChange(Activity activity, int newState) {
-                if (newState == ActivityState.RESUMED) {
-                    DownloadUtils.checkForExternallyRemovedDownloads(
-                            mManager.getBackendProvider(), host.isIncognito());
-                }
+        mActivityStateListener = (activity1, newState) -> {
+            if (newState == ActivityState.RESUMED) {
+                DownloadUtils.checkForExternallyRemovedDownloads(
+                        mManager.getBackendProvider(), host.isIncognito());
             }
         };
         ApplicationStatus.registerStateListenerForActivity(mActivityStateListener, activity);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSheetContent.java
index 69aef96..c0e6523 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSheetContent.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.download;
 
-import android.app.Activity;
 import android.view.View;
 
 import org.chromium.base.ActivityState;
@@ -62,13 +61,10 @@
         // own ActivityStateListener. If multiple tabs are showing the downloads page, multiple
         // requests to check for externally removed downloads will be issued when the activity is
         // resumed.
-        mActivityStateListener = new ActivityStateListener() {
-            @Override
-            public void onActivityStateChange(Activity activity, int newState) {
-                if (newState == ActivityState.RESUMED) {
-                    DownloadUtils.checkForExternallyRemovedDownloads(
-                            mDownloadManager.getBackendProvider(), isIncognito);
-                }
+        mActivityStateListener = (activity1, newState) -> {
+            if (newState == ActivityState.RESUMED) {
+                DownloadUtils.checkForExternallyRemovedDownloads(
+                        mDownloadManager.getBackendProvider(), isIncognito);
             }
         };
         ApplicationStatus.registerStateListenerForActivity(mActivityStateListener, activity);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index da6478dc..d964c17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -45,6 +45,7 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.offlinepages.DownloadUiActionFlags;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.OfflinePageOrigin;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -230,17 +231,19 @@
      * @param context Context to pull resources from.
      */
     public static void downloadOfflinePage(Context context, Tab tab) {
+        OfflinePageOrigin origin = new OfflinePageOrigin(context, tab);
+
         if (tab.isShowingErrorPage()) {
             // The download needs to be scheduled to happen at later time due to current network
             // error.
             final OfflinePageBridge bridge = OfflinePageBridge.getForProfile(tab.getProfile());
             bridge.scheduleDownload(tab.getWebContents(), OfflinePageBridge.ASYNC_NAMESPACE,
-                    tab.getUrl(), DownloadUiActionFlags.PROMPT_DUPLICATE);
+                    tab.getUrl(), DownloadUiActionFlags.PROMPT_DUPLICATE, origin);
         } else {
             // Otherwise, the download can be started immediately.
             final OfflinePageDownloadBridge bridge =
                     new OfflinePageDownloadBridge(tab.getProfile());
-            bridge.startDownload(tab);
+            bridge.startDownload(tab, origin);
             bridge.destroy();
             DownloadUtils.recordDownloadPageMetrics(tab);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index 69d8742..3853a285 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -477,16 +477,13 @@
         textView = (TextView) v.findViewById(R.id.oma_download_description);
         textView.setText(omaInfo.getValue(OMA_DESCRIPTION));
 
-        DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (which == AlertDialog.BUTTON_POSITIVE) {
-                    downloadOMAContent(downloadId, downloadInfo, omaInfo);
-                } else {
-                    sendNotification(omaInfo, downloadInfo,
-                            DownloadItem.INVALID_DOWNLOAD_ID,
-                            DOWNLOAD_STATUS_USER_CANCELLED);
-                }
+        DialogInterface.OnClickListener clickListener = (dialog, which) -> {
+            if (which == AlertDialog.BUTTON_POSITIVE) {
+                downloadOMAContent(downloadId, downloadInfo, omaInfo);
+            } else {
+                sendNotification(omaInfo, downloadInfo,
+                        DownloadItem.INVALID_DOWNLOAD_ID,
+                        DOWNLOAD_STATUS_USER_CANCELLED);
             }
         };
         new AlertDialog.Builder(
@@ -512,13 +509,10 @@
     private void showDownloadWarningDialog(
             int titleId, final OMAInfo omaInfo, final DownloadInfo downloadInfo,
             final String statusMessage) {
-        DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (which == AlertDialog.BUTTON_POSITIVE) {
-                    sendInstallNotificationAndNextStep(omaInfo, downloadInfo,
-                            DownloadItem.INVALID_DOWNLOAD_ID, statusMessage);
-                }
+        DialogInterface.OnClickListener clickListener = (dialog, which) -> {
+            if (which == AlertDialog.BUTTON_POSITIVE) {
+                sendInstallNotificationAndNextStep(omaInfo, downloadInfo,
+                        DownloadItem.INVALID_DOWNLOAD_ID, statusMessage);
             }
         };
         new AlertDialog.Builder(
@@ -540,16 +534,13 @@
         }
         final String nextUrl = omaInfo.getValue(OMA_NEXT_URL);
         final Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
-        DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (which == AlertDialog.BUTTON_POSITIVE) {
-                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(nextUrl));
-                    intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
-                    intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
-                    intent.setPackage(mContext.getPackageName());
-                    activity.startActivity(intent);
-                }
+        DialogInterface.OnClickListener clickListener = (dialog, which) -> {
+            if (which == AlertDialog.BUTTON_POSITIVE) {
+                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(nextUrl));
+                intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
+                intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+                intent.setPackage(mContext.getPackageName());
+                activity.startActivity(intent);
             }
         };
         new AlertDialog.Builder(activity)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index c2f25be..4062e11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -497,6 +497,7 @@
     /**
      * Schedules to download a page from |url| and categorize under |nameSpace|.
      * The duplicate pages or requests will be checked.
+     * Origin is presumed to be Chrome.
      *
      * @param webContents Web contents upon which the infobar is shown.
      * @param nameSpace Namespace of the page to save.
@@ -505,7 +506,23 @@
      */
     public void scheduleDownload(
             WebContents webContents, String nameSpace, String url, int uiAction) {
-        nativeScheduleDownload(mNativeOfflinePageBridge, webContents, nameSpace, url, uiAction);
+        scheduleDownload(webContents, nameSpace, url, uiAction, new OfflinePageOrigin());
+    }
+
+    /**
+     * Schedules to download a page from |url| and categorize under |namespace| from |origin|.
+     * The duplicate pages or requests will be checked.
+     *
+     * @param webContents Web contents upon which the infobar is shown.
+     * @param nameSpace Namespace of the page to save.
+     * @param url URL of the page to save.
+     * @param uiAction UI action, like showing infobar or toast on certain case.
+     * @param origin Origin of the page.
+     */
+    public void scheduleDownload(WebContents webContents, String nameSpace, String url,
+            int uiAction, OfflinePageOrigin origin) {
+        nativeScheduleDownload(mNativeOfflinePageBridge, webContents, nameSpace, url, uiAction,
+                origin.encodeAsJsonString());
     }
 
     /**
@@ -577,17 +594,18 @@
     @CalledByNative
     private static void createOfflinePageAndAddToList(List<OfflinePageItem> offlinePagesList,
             String url, long offlineId, String clientNamespace, String clientId, String filePath,
-            long fileSize, long creationTime, int accessCount, long lastAccessTimeMs) {
+            long fileSize, long creationTime, int accessCount, long lastAccessTimeMs,
+            String requestOrigin) {
         offlinePagesList.add(createOfflinePageItem(url, offlineId, clientNamespace, clientId,
-                filePath, fileSize, creationTime, accessCount, lastAccessTimeMs));
+                filePath, fileSize, creationTime, accessCount, lastAccessTimeMs, requestOrigin));
     }
 
     @CalledByNative
     private static OfflinePageItem createOfflinePageItem(String url, long offlineId,
             String clientNamespace, String clientId, String filePath, long fileSize,
-            long creationTime, int accessCount, long lastAccessTimeMs) {
-        return new OfflinePageItem(url, offlineId, clientNamespace, clientId, filePath,
-                fileSize, creationTime, accessCount, lastAccessTimeMs);
+            long creationTime, int accessCount, long lastAccessTimeMs, String requestOrigin) {
+        return new OfflinePageItem(url, offlineId, clientNamespace, clientId, filePath, fileSize,
+                creationTime, accessCount, lastAccessTimeMs, requestOrigin);
     }
 
     @CalledByNative
@@ -650,7 +668,7 @@
     private native boolean nativeIsShowingDownloadButtonInErrorPage(
             long nativeOfflinePageBridge, WebContents webContents);
     private native void nativeScheduleDownload(long nativeOfflinePageBridge,
-            WebContents webContents, String nameSpace, String url, int uiAction);
+            WebContents webContents, String nameSpace, String url, int uiAction, String origin);
     private native boolean nativeIsOfflinePage(
             long nativeOfflinePageBridge, WebContents webContents);
     private native OfflinePageItem nativeGetOfflinePage(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java
index 75af288d..3330d99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java
@@ -18,10 +18,11 @@
     private final long mCreationTimeMs;
     private final int mAccessCount;
     private final long mLastAccessTimeMs;
+    private final String mRequestOrigin;
 
     public OfflinePageItem(String url, long offlineId, String clientNamespace, String clientId,
             String filePath, long fileSize, long creationTimeMs, int accessCount,
-            long lastAccessTimeMs) {
+            long lastAccessTimeMs, String requestOrigin) {
         mUrl = url;
         mOfflineId = offlineId;
         mClientId = new ClientId(clientNamespace, clientId);
@@ -30,6 +31,7 @@
         mCreationTimeMs = creationTimeMs;
         mAccessCount = accessCount;
         mLastAccessTimeMs = lastAccessTimeMs;
+        mRequestOrigin = requestOrigin;
     }
 
     /** @return URL of the offline page. */
@@ -79,4 +81,10 @@
     public long getLastAccessTimeMs() {
         return mLastAccessTimeMs;
     }
+
+    /** @return The originating application of the request. */
+    @VisibleForTesting
+    public String getRequestOrigin() {
+        return mRequestOrigin;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java
new file mode 100644
index 0000000..8b55c24
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java
@@ -0,0 +1,109 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.offlinepages;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.text.TextUtils;
+import android.util.Base64;
+
+import org.json.JSONArray;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.tab.Tab;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * Class encapsulating the application origin of a particular offline page request.
+ */
+public class OfflinePageOrigin {
+    private final String mAppName;
+    private final String[] mSignatures;
+
+    /** Creates origin based on the context and tab. */
+    public OfflinePageOrigin(Context context, Tab tab) {
+        if (TextUtils.isEmpty(tab.getAppAssociatedWith())) {
+            mAppName = "";
+        } else {
+            mAppName = tab.getAppAssociatedWith();
+        }
+
+        mSignatures = getAppSignaturesFor(context, mAppName);
+    }
+
+    /** Creates a Chrome origin. */
+    public OfflinePageOrigin() {
+        this("", null);
+    }
+
+    @VisibleForTesting
+    OfflinePageOrigin(String appName, String[] signatures) {
+        mAppName = appName;
+        mSignatures = signatures;
+    }
+
+    /**
+     * Encode the origin information into a JSON string of
+     * [appName, [SHA-256 encoded signature, SHA-256 encoded signature...]]
+     *
+     * @return The JSON encoded origin information or empty string if there is
+     *         no app information (ie assuming chrome).
+     */
+    public String encodeAsJsonString() {
+        // We default to "", implying chrome-only if inputs invalid.
+        if (TextUtils.isEmpty(mAppName) || mSignatures == null) return "";
+        // JSONArray(Object[]) requires API 19
+        JSONArray signatureArray = new JSONArray();
+        for (String s : mSignatures) signatureArray.put(s);
+        return new JSONArray().put(mAppName).put(signatureArray).toString();
+    }
+
+    /**
+     * @param context The context to look up signatures.
+     * @param appName The name of the application to look up.
+     * @return a sorted list of strings representing the signatures of an app.
+     *          Null if the app name is invalid or cannot be found.
+     */
+    private static String[] getAppSignaturesFor(Context context, String appName) {
+        if (TextUtils.isEmpty(appName)) return null;
+        try {
+            PackageManager packageManager = context.getPackageManager();
+            Signature[] signatureList =
+                    packageManager.getPackageInfo(appName, PackageManager.GET_SIGNATURES)
+                            .signatures;
+            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+            String[] sigStrings = new String[signatureList.length];
+            for (int i = 0; i < sigStrings.length; i++) {
+                messageDigest.update(signatureList[i].toByteArray());
+
+                // The digest is reset after completing the hash computation.
+                sigStrings[i] = byteArrayToString(messageDigest.digest());
+            }
+            Arrays.sort(sigStrings);
+            return sigStrings;
+        } catch (NameNotFoundException e) {
+            return null; // Cannot find the app anymore. No signatures.
+        } catch (NoSuchAlgorithmException e) {
+            return null; // Cannot find the SHA-256 encryption algorithm. Shouldn't happen.
+        }
+    }
+
+    /**
+     * Formats bytes into a string for easier comparison.
+     *
+     * @param input Input bytes.
+     * @return A string representation of the input bytes, e.g., "0123456789abcdefg"
+     */
+    private static String byteArrayToString(byte[] input) {
+        if (input == null) return null;
+
+        return Base64.encodeToString(input, Base64.DEFAULT);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
index 82894b9f..9684c9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.download.DownloadItem;
 import org.chromium.chrome.browser.download.DownloadServiceDelegate;
 import org.chromium.chrome.browser.download.ui.BackendProvider.OfflinePageDelegate;
+import org.chromium.chrome.browser.offlinepages.OfflinePageOrigin;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -215,10 +216,12 @@
      * background and killed, the background request remains that will
      * eventually load the page in background and obtain its offline
      * snapshot.
+     *
      * @param tab a tab contents of which will be saved locally.
+     * @param origin the object encapsulating application origin of the request.
      */
-    public void startDownload(Tab tab) {
-        nativeStartDownload(mNativeOfflinePageDownloadBridge, tab);
+    public void startDownload(Tab tab, OfflinePageOrigin origin) {
+        nativeStartDownload(mNativeOfflinePageDownloadBridge, tab, origin.encodeAsJsonString());
     }
 
     /**
@@ -319,6 +322,6 @@
     native void nativeResumeDownload(long nativeOfflinePageDownloadBridge, String guid);
     native void nativeDeleteItemByGuid(long nativeOfflinePageDownloadBridge, String guid);
     native long nativeGetOfflineIdByGuid(long nativeOfflinePageDownloadBridge, String guid);
-    native void nativeStartDownload(long nativeOfflinePageDownloadBridge, Tab tab);
+    native void nativeStartDownload(long nativeOfflinePageDownloadBridge, Tab tab, String origin);
     native void nativeResumePendingRequestImmediately(long nativeOfflinePageDownloadBridge);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
index 6b56beb9..1e8d388 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
@@ -21,9 +21,7 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-
 import java.text.SimpleDateFormat;
-
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -248,16 +246,17 @@
     @CalledByNative
     private static void createOfflinePageAndAddToList(List<OfflinePageItem> offlinePagesList,
             String url, long offlineId, String clientNamespace, String clientId, String filePath,
-            long fileSize, long creationTime, int accessCount, long lastAccessTimeMs) {
+            long fileSize, long creationTime, int accessCount, long lastAccessTimeMs,
+            String requestOrigin) {
         offlinePagesList.add(createOfflinePageItem(url, offlineId, clientNamespace, clientId,
-                filePath, fileSize, creationTime, accessCount, lastAccessTimeMs));
+                filePath, fileSize, creationTime, accessCount, lastAccessTimeMs, requestOrigin));
     }
 
     private static OfflinePageItem createOfflinePageItem(String url, long offlineId,
             String clientNamespace, String clientId, String filePath, long fileSize,
-            long creationTime, int accessCount, long lastAccessTimeMs) {
+            long creationTime, int accessCount, long lastAccessTimeMs, String requestOrigin) {
         return new OfflinePageItem(url, offlineId, clientNamespace, clientId, filePath, fileSize,
-                creationTime, accessCount, lastAccessTimeMs);
+                creationTime, accessCount, lastAccessTimeMs, requestOrigin);
     }
 
     private native long nativeCreateBridgeForProfile(
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index f682b1f..9c2c92f 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -691,6 +691,7 @@
   "java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java",
+  "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java",
   "java/src/org/chromium/chrome/browser/offlinepages/SavePageRequest.java",
@@ -1801,6 +1802,7 @@
   "junit/src/org/chromium/chrome/browser/offlinepages/ClientIdTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageOriginUnitTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java",
   "junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java
index 77b9c75a..85576a58 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java
@@ -67,14 +67,9 @@
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
         mActivityTestRule.loadUrl("about:blank");
         ChromeDownloadDelegate delegate = ThreadUtils.runOnUiThreadBlockingNoException(
-                new Callable<ChromeDownloadDelegate>() {
-                    @Override
-                    public ChromeDownloadDelegate call() {
-                        return new MockChromeDownloadDelegate(
-                                InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                                tab);
-                    }
-                });
+                (Callable<ChromeDownloadDelegate>) () -> new MockChromeDownloadDelegate(
+                        InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                        tab));
         Assert.assertFalse(delegate.shouldInterceptContextMenuDownload("file://test/test.html"));
         Assert.assertFalse(delegate.shouldInterceptContextMenuDownload("http://test/test.html"));
         Assert.assertFalse(delegate.shouldInterceptContextMenuDownload("ftp://test/test.dm"));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
index 050810c3..1b379573c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
@@ -66,43 +66,23 @@
         public void onChanged() {
             // To guarantee that all real Observers have had a chance to react to the event, post
             // the CallbackHelper.notifyCalled() call.
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onChangedCallback.notifyCalled();
-                }
-            });
+            mHandler.post(() -> onChangedCallback.notifyCalled());
         }
 
         @Override
         public void onSelectionStateChange(List<DownloadHistoryItemWrapper> selectedItems) {
             mOnSelectionItems = selectedItems;
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onSelectionCallback.notifyCalled();
-                }
-            });
+            mHandler.post(() -> onSelectionCallback.notifyCalled());
         }
 
         @Override
         public void onFilterChanged(int filter) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onFilterCallback.notifyCalled();
-                }
-            });
+            mHandler.post(() -> onFilterCallback.notifyCalled());
         }
 
         @Override
         public void onSpaceDisplayUpdated(SpaceDisplay display) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onSpaceDisplayUpdatedCallback.notifyCalled();
-                }
-            });
+            mHandler.post(() -> onSpaceDisplayUpdatedCallback.notifyCalled());
         }
 
         @Override
@@ -164,12 +144,7 @@
         int callCount = mAdapterObserver.onChangedCallback.getCallCount();
         int spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final DownloadItem updateItem = StubbedProvider.createDownloadItem(7, "20151021 07:28");
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAdapter.onDownloadItemCreated(updateItem);
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> mAdapter.onDownloadItemCreated(updateItem));
         mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
         assertEquals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
@@ -179,12 +154,7 @@
         spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final DownloadItem deletedItem = StubbedProvider.createDownloadItem(6, "20151021 07:28");
         deletedItem.setHasBeenExternallyRemoved(true);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAdapter.onDownloadItemUpdated(deletedItem);
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> mAdapter.onDownloadItemUpdated(deletedItem));
         mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
         assertEquals("5.50 GB downloaded", mSpaceUsedDisplay.getText());
@@ -194,13 +164,9 @@
         spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final OfflinePageDownloadItem deletedPage =
                 StubbedProvider.createOfflineItem(3, "20151021 07:28");
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mStubbedProvider.getOfflinePageBridge().observer.onItemDeleted(
-                        deletedPage.getGuid());
-            }
-        });
+        ThreadUtils.runOnUiThread(
+                () -> mStubbedProvider.getOfflinePageBridge().observer.onItemDeleted(
+                        deletedPage.getGuid()));
         mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
         assertEquals("512.00 MB downloaded", mSpaceUsedDisplay.getText());
@@ -262,13 +228,8 @@
         assertEquals(0,
                 mStubbedProvider.getDownloadDelegate().removeDownloadCallback.getCallCount());
         assertEquals(0, mStubbedProvider.getOfflinePageBridge().deleteItemCallback.getCallCount());
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
-                        .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0));
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
+                .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0)));
 
         mStubbedProvider.getDownloadDelegate().removeDownloadCallback.waitForCallback(0);
         assertEquals(1,
@@ -297,12 +258,9 @@
         int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final DownloadItem item7 = StubbedProvider.createDownloadItem(7, "20161021 07:28");
         final DownloadItem item8 = StubbedProvider.createDownloadItem(8, "20161021 17:28");
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAdapter.onDownloadItemCreated(item7);
-                mAdapter.onDownloadItemCreated(item8);
-            }
+        ThreadUtils.runOnUiThread(() -> {
+            mAdapter.onDownloadItemCreated(item7);
+            mAdapter.onDownloadItemCreated(item8);
         });
 
         // The criteria is needed because an AsyncTask is fired to update the space display, which
@@ -323,13 +281,8 @@
 
         // Click the delete button.
         callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
-                        .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0));
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
+                .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0)));
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
 
         // Assert that items are temporarily removed from the adapter. The two selected items,
@@ -341,12 +294,8 @@
         callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final View rootView = mUi.getView().getRootView();
         assertNotNull(rootView.findViewById(R.id.snackbar));
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                rootView.findViewById(R.id.snackbar_button).callOnClick();
-            }
-        });
+        ThreadUtils.runOnUiThread(
+                (Runnable) () -> rootView.findViewById(R.id.snackbar_button).callOnClick());
 
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
 
@@ -376,12 +325,9 @@
         int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final DownloadItem item7 = StubbedProvider.createDownloadItem(7, "20161021 07:28");
         final DownloadItem item8 = StubbedProvider.createDownloadItem(8, "20161021 17:28");
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAdapter.onDownloadItemCreated(item7);
-                mAdapter.onDownloadItemCreated(item8);
-            }
+        ThreadUtils.runOnUiThread(() -> {
+            mAdapter.onDownloadItemCreated(item7);
+            mAdapter.onDownloadItemCreated(item8);
         });
 
         // The criteria is needed because an AsyncTask is fired to update the space display, which
@@ -402,13 +348,8 @@
 
         // Click the delete button.
         callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
-                        .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0));
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> assertTrue(mUi.getDownloadManagerToolbarForTests().getMenu()
+                .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0)));
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
 
         // Assert that the two items and their date bucket are temporarily removed from the adapter.
@@ -419,12 +360,8 @@
         callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
         final View rootView = mUi.getView().getRootView();
         assertNotNull(rootView.findViewById(R.id.snackbar));
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                rootView.findViewById(R.id.snackbar_button).callOnClick();
-            }
-        });
+        ThreadUtils.runOnUiThread(
+                (Runnable) () -> rootView.findViewById(R.id.snackbar_button).callOnClick());
 
         mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
 
@@ -460,12 +397,7 @@
                 shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM));
 
         // Scroll to ensure the item at position 8 is visible.
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.scrollToPosition(9);
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(9));
         getInstrumentation().waitForIdleSync();
 
         // Select another image, download item #0.
@@ -478,12 +410,7 @@
                 2, shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM).size());
 
         // Scroll to ensure the item at position 5 is visible.
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.scrollToPosition(6);
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(6));
         getInstrumentation().waitForIdleSync();
 
         // Select non-image item, download item #4.
@@ -496,12 +423,7 @@
                 3, shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM).size());
 
         // Scroll to ensure the item at position 2 is visible.
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.scrollToPosition(3);
-            }
-        });
+        ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(3));
         getInstrumentation().waitForIdleSync();
 
         // Select an offline page #3.
@@ -564,12 +486,8 @@
         assertTrue(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
 
         int callCount = mAdapterObserver.onSelectionCallback.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                toolbar.getMenu().performIdentifierAction(R.id.search_menu_id, 0);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> toolbar.getMenu().performIdentifierAction(R.id.search_menu_id, 0));
 
         // The selection should be cleared when a search is started.
         mAdapterObserver.onSelectionCallback.waitForCallback(callCount, 1);
@@ -587,12 +505,7 @@
         assertEquals(View.VISIBLE, toolbarSearchView.getVisibility());
 
         // Close the search view.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                toolbar.onNavigationBack();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> toolbar.onNavigationBack());
         assertEquals(View.GONE, toolbarSearchView.getVisibility());
     }
 
@@ -626,12 +539,9 @@
     private void clickOnFilter(final DownloadManagerUi ui, final int position) throws Exception {
         int previousCount = mAdapterObserver.onChangedCallback.getCallCount();
         final Spinner spinner = mUi.getDownloadManagerToolbarForTests().getSpinnerForTests();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                spinner.performClick();
-                spinner.setSelection(position);
-            }
+        ThreadUtils.runOnUiThread(() -> {
+            spinner.performClick();
+            spinner.setSelection(position);
         });
         mAdapterObserver.onChangedCallback.waitForCallback(previousCount);
     }
@@ -643,12 +553,7 @@
         final DownloadItemView itemView =
                 ((DownloadHistoryItemViewHolder) mostRecentHolder).getItemView();
 
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                itemView.performLongClick();
-            }
-        });
+        ThreadUtils.runOnUiThread((Runnable) () -> itemView.performLongClick());
         mAdapterObserver.onSelectionCallback.waitForCallback(callCount, 1);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index 76e1d44..192a4f5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -88,14 +88,11 @@
          * Helper method to simulate that the DownloadNotificationService is connected.
          */
         public void onServiceConnected() {
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    mService = new MockDownloadNotificationService();
-                    mService.setContext(new AdvancedMockContext(
-                            mContext.getApplicationContext()));
-                    mService.onCreate();
-                }
+            ThreadUtils.runOnUiThreadBlocking(() -> {
+                mService = new MockDownloadNotificationService();
+                mService.setContext(new AdvancedMockContext(
+                        mContext.getApplicationContext()));
+                mService.onCreate();
             });
             setDownloadNotificationService(mService);
         }
@@ -281,12 +278,8 @@
 
         @Override
         protected void scheduleUpdateIfNeeded() {
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    DownloadManagerServiceForTest.super.scheduleUpdateIfNeeded();
-                }
-            });
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> DownloadManagerServiceForTest.super.scheduleUpdateIfNeeded());
         }
 
         @Override
@@ -412,12 +405,8 @@
         MockDownloadSnackbarController snackbarController = new MockDownloadSnackbarController();
         final DownloadManagerServiceForTest dService = new DownloadManagerServiceForTest(
                 getTestContext(), notifier, UPDATE_DELAY_FOR_TEST);
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadManagerService.setDownloadManagerService(dService);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> DownloadManagerService.setDownloadManagerService(dService));
         dService.setDownloadSnackbarController(snackbarController);
         // Try calling download completed directly.
         DownloadInfo successful = getDownloadInfo();
@@ -447,12 +436,8 @@
         MockDownloadSnackbarController snackbarController = new MockDownloadSnackbarController();
         final DownloadManagerServiceForTest dService = new DownloadManagerServiceForTest(
                 getTestContext(), notifier, UPDATE_DELAY_FOR_TEST);
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadManagerService.setDownloadManagerService(dService);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> DownloadManagerService.setDownloadManagerService(dService));
         dService.setDownloadSnackbarController(snackbarController);
         // Check that if an interrupted download cannot be resumed, it will trigger a download
         // failure.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
index 0e7e524..abb6586 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
@@ -98,12 +98,8 @@
 
     @Override
     protected void shutdownService() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadNotificationServiceTest.super.shutdownService();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> DownloadNotificationServiceTest.super.shutdownService());
     }
 
     @Override
@@ -116,12 +112,9 @@
     }
 
     private void startNotificationService() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
-                startService(intent);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
+            startService(intent);
         });
     }
 
@@ -138,12 +131,7 @@
     }
 
     private void resumeAllDownloads(final DownloadNotificationService service) throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                service.resumeAllPendingDownloads();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> service.resumeAllPendingDownloads());
     }
 
     /**
@@ -155,12 +143,7 @@
     public void testPausingWithoutOngoingDownloads() {
         setupService();
         startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> getService().updateNotificationsForShutdown());
         assertTrue(getService().isPaused());
         assertTrue(getService().getNotificationIds().isEmpty());
     }
@@ -199,13 +182,10 @@
                 getSystemContext().getApplicationContext());
         DownloadResumptionScheduler.setDownloadResumptionScheduler(scheduler);
         setupService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
-                intent.setAction(DownloadNotificationService.ACTION_DOWNLOAD_RESUME_ALL);
-                startService(intent);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Intent intent = new Intent(getService(), MockDownloadNotificationService.class);
+            intent.setAction(DownloadNotificationService.ACTION_DOWNLOAD_RESUME_ALL);
+            startService(intent);
         });
         assertFalse(scheduler.mScheduled);
     }
@@ -251,12 +231,7 @@
                 DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> getService().updateNotificationsForShutdown());
         assertTrue(getService().isPaused());
         assertEquals(2, getService().getNotificationIds().size());
         assertTrue(getService().getNotificationIds().contains(1));
@@ -285,12 +260,7 @@
                 DownloadSharedPreferenceHelper.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().updateNotificationsForShutdown();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> getService().updateNotificationsForShutdown());
         assertEquals(2, getService().getNotificationIds().size());
         assertTrue(getService().getNotificationIds().contains(3));
         assertTrue(getService().getNotificationIds().contains(4));
@@ -379,12 +349,8 @@
 
         final MockDownloadManagerService manager =
                 new MockDownloadManagerService(getSystemContext().getApplicationContext());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadManagerService.setDownloadManagerService(manager);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> DownloadManagerService.setDownloadManagerService(manager));
         DownloadManagerService.setIsNetworkMeteredForTest(true);
         resumeAllDownloads(service);
         assertEquals(1, manager.mDownloads.size());
@@ -417,12 +383,7 @@
         editor.apply();
         startNotificationService();
 
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                getService().onTaskRemoved(new Intent());
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> getService().onTaskRemoved(new Intent()));
 
         assertTrue(getService().isPaused());
         assertFalse(sharedPrefs.contains(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
index 87c5b20e..74d9d5df 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
@@ -47,7 +47,6 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
 
 /**
  * Tests Chrome download feature by attempting to download some files.
@@ -175,12 +174,8 @@
         Assert.assertTrue(mDownloadTestRule.hasDownload(FILENAME_GZIP, null));
 
         CriteriaHelper.pollUiThread(
-                Criteria.equals(initialTabCount, new Callable<Integer>() {
-                    @Override
-                    public Integer call() {
-                        return mDownloadTestRule.getActivity().getCurrentTabModel().getCount();
-                    }
-                }));
+                Criteria.equals(initialTabCount,
+                        () -> mDownloadTestRule.getActivity().getCurrentTabModel().getCount()));
     }
 
     @Test
@@ -330,12 +325,8 @@
         final TabModel model = mDownloadTestRule.getActivity().getCurrentTabModel();
         final int count = model.getCount();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                TabModelUtils.setIndex(model, count - 1);
-            }
-        });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                (Runnable) () -> TabModelUtils.setIndex(model, count - 1));
 
         CriteriaHelper.pollUiThread(new Criteria() {
             @Override
@@ -438,13 +429,9 @@
         try {
             final DownloadManagerRequestInterceptorForTest interceptor =
                     new DownloadManagerRequestInterceptorForTest();
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    DownloadManagerService.getDownloadManagerService()
-                            .setDownloadManagerRequestInterceptor(interceptor);
-                }
-            });
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> DownloadManagerService.getDownloadManagerService()
+                            .setDownloadManagerRequestInterceptor(interceptor));
             List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
             headers.add(Pair.create("Content-Type", "application/vnd.oma.drm.message"));
             final String url = webServer.setResponse("/test.dm", "testdata", headers);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
index 7cbb5a49..1781927 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestRule.java
@@ -230,26 +230,20 @@
         ApplicationUtils.waitForLibraryDependencies(getInstrumentation());
         final Context context = getInstrumentation().getTargetContext().getApplicationContext();
 
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mSavedDownloadManagerService = DownloadManagerService.setDownloadManagerService(
-                        new TestDownloadManagerService(context, new SystemDownloadNotifier(context),
-                                new Handler(), UPDATE_DELAY_MILLIS));
-                DownloadController.setDownloadNotificationService(
-                        DownloadManagerService.getDownloadManagerService());
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mSavedDownloadManagerService = DownloadManagerService.setDownloadManagerService(
+                    new TestDownloadManagerService(context, new SystemDownloadNotifier(context),
+                            new Handler(), UPDATE_DELAY_MILLIS));
+            DownloadController.setDownloadNotificationService(
+                    DownloadManagerService.getDownloadManagerService());
         });
     }
 
     private void tearDown() throws Exception {
         cleanUpAllDownloads();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                DownloadManagerService.setDownloadManagerService(mSavedDownloadManagerService);
-                DownloadController.setDownloadNotificationService(mSavedDownloadManagerService);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            DownloadManagerService.setDownloadManagerService(mSavedDownloadManagerService);
+            DownloadController.setDownloadNotificationService(mSavedDownloadManagerService);
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
index ea483c9..1add6f5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
@@ -15,7 +15,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
 
 /**
  * Mock class to DownloadNotificationService for testing purpose.
@@ -100,14 +99,10 @@
             final String fileName, final long systemDownloadId, final boolean isOffTheRecord,
             final boolean isSupportedMimeType, final boolean isOpenable, final Bitmap icon,
             final String originalUrl, final String referrer) {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Integer>() {
-            @Override
-            public Integer call() throws Exception {
-                return MockDownloadNotificationService.super.notifyDownloadSuccessful(id, filePath,
+        return ThreadUtils.runOnUiThreadBlockingNoException(
+                () -> MockDownloadNotificationService.super.notifyDownloadSuccessful(id, filePath,
                         fileName, systemDownloadId, isOffTheRecord, isSupportedMimeType, isOpenable,
-                        icon, originalUrl, referrer);
-            }
-        });
+                        icon, originalUrl, referrer));
     }
 
     @Override
@@ -115,34 +110,24 @@
             final Progress progress, final long bytesReceived, final long timeRemainingInMillis,
             final long startTime, final boolean isOffTheRecord,
             final boolean canDownloadWhileMetered, final boolean isTransient, final Bitmap icon) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadProgress(id, fileName, progress,
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> MockDownloadNotificationService.super.notifyDownloadProgress(id, fileName,
+                        progress,
                         bytesReceived, timeRemainingInMillis, startTime, isOffTheRecord,
-                        canDownloadWhileMetered, isTransient, icon);
-            }
-        });
+                        canDownloadWhileMetered, isTransient, icon));
     }
 
     @Override
     public void notifyDownloadFailed(final ContentId id, final String fileName, final Bitmap icon) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadFailed(id, fileName, icon);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> MockDownloadNotificationService.super.notifyDownloadFailed(id, fileName,
+                        icon));
     }
 
     @Override
     public void notifyDownloadCanceled(final ContentId id) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                MockDownloadNotificationService.super.notifyDownloadCanceled(id);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> MockDownloadNotificationService.super.notifyDownloadCanceled(id));
     }
 }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
index a0bdf15..9f8e2843 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java
@@ -57,13 +57,9 @@
         @Override
         void updateDownloadNotification(
                 final PendingNotificationInfo notificationInfo, final boolean autoRelease) {
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    MockSystemDownloadNotifier.super.updateDownloadNotification(
-                            notificationInfo, autoRelease);
-                }
-            });
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> MockSystemDownloadNotifier.super.updateDownloadNotification(
+                            notificationInfo, autoRelease));
         }
     }
 
@@ -77,16 +73,13 @@
      * Helper method to simulate that the DownloadNotificationService is connected.
      */
     private void onServiceConnected() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mService = new MockDownloadNotificationService();
-                mService.setContext(
-                        new AdvancedMockContext(InstrumentationRegistry.getInstrumentation()
-                                                        .getTargetContext()
-                                                        .getApplicationContext()));
-                mService.onCreate();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mService = new MockDownloadNotificationService();
+            mService.setContext(
+                    new AdvancedMockContext(InstrumentationRegistry.getInstrumentation()
+                            .getTargetContext()
+                            .getApplicationContext()));
+            mService.onCreate();
         });
         mDownloadNotifier.setDownloadNotificationService(mService);
         mDownloadNotifier.handlePendingNotifications();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index c06f1b44..def3258 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.SavePageCallback;
+import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -359,6 +360,48 @@
                 offlineIdToIgnore, asyncPages.get(0).getOfflineId());
     }
 
+    @Test
+    @SmallTest
+    @RetryOnFailure
+    public void testDownloadPage() throws Exception {
+        final OfflinePageOrigin origin =
+                new OfflinePageOrigin("abc.xyz", new String[] {"deadbeef"});
+        mActivityTestRule.loadUrl(mTestPage);
+        final String originString = origin.encodeAsJsonString();
+        final Semaphore semaphore = new Semaphore(0);
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                Assert.assertNotNull(
+                        "Tab is null", mActivityTestRule.getActivity().getActivityTab());
+                Assert.assertEquals("URL does not match requested.", mTestPage,
+                        mActivityTestRule.getActivity().getActivityTab().getUrl());
+                Assert.assertNotNull("WebContents is null",
+                        mActivityTestRule.getActivity().getActivityTab().getWebContents());
+
+                // Use Downloadbridge, because scheduler does not work in test.
+                OfflinePageDownloadBridge downloadBridge =
+                        new OfflinePageDownloadBridge(Profile.getLastUsedProfile());
+
+                mOfflinePageBridge.addObserver(new OfflinePageModelObserver() {
+                    @Override
+                    public void offlinePageAdded(OfflinePageItem newPage) {
+                        mOfflinePageBridge.removeObserver(this);
+                        semaphore.release();
+                    }
+                });
+
+                downloadBridge.startDownload(
+                        mActivityTestRule.getActivity().getActivityTab(), origin);
+            }
+        });
+        Assert.assertTrue("Semaphore acquire failed. Timed out.",
+                semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        List<OfflinePageItem> pages = getAllPages();
+        Assert.assertEquals(originString, pages.get(0).getRequestOrigin());
+    }
+
     // Returns offline ID.
     private long savePage(final int expectedResult, final String expectedUrl)
             throws InterruptedException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
index 4809a42..1021786e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
@@ -87,23 +87,6 @@
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
                 DialogInterface.BUTTON_POSITIVE, mPaymentRequestTestRule.getDismissed());
 
-        // Make sure all the steps were logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Initiated", 1));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Shown", 1));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.PayClicked", 1));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", 1));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Completed", 1));
-
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
@@ -394,14 +377,6 @@
         mPaymentRequestTestRule.triggerUIAndWait(
                 "androidPaySkipUiBuy", mPaymentRequestTestRule.getResultReady());
 
-        // The "SkippedShow" step should be logged instead of "Shown".
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.SkippedShow", 1));
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Shown", 1));
-
         assertOnlySpecificSelectedPaymentMethodMetricLogged(SelectedPaymentMethod.ANDROID_PAY);
 
         // Make sure the events were logged correctly.
@@ -435,14 +410,6 @@
                 R.id.close_button, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
-        // The "Shown" step should be logged, not "SkippedShow".
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Shown", 1));
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.SkippedShow", 1));
-
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS;
@@ -480,11 +447,6 @@
                 R.id.close_button, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
-        // Make sure "Shown" is logged only once.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CheckoutFunnel.Shown", 1));
-
         // Make sure only one set of events was logged.
         Assert.assertEquals(
                 1, RecordHistogram.getHistogramTotalCountForTesting("PaymentRequest.Events"));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
index b0e57acdf..eb549e7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -889,7 +889,7 @@
     }
 
     private OfflinePageItem createOfflinePageItem(String url, long offlineId) {
-        return new OfflinePageItem(url, offlineId, "", "", "", 0, 0, 0, 0);
+        return new OfflinePageItem(url, offlineId, "", "", "", 0, 0, 0, 0, "");
     }
 
     private void verifyAction(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
index fe5a1d260..d65ee2f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
@@ -52,10 +52,11 @@
     private static final long TEST_CREATIONTIMEMS = 150;
     private static final int TEST_ACCESSCOUNT = 1;
     private static final long TEST_LASTACCESSTIMEMS = 20160314;
+    private static final String TEST_REQUEST_ORIGIN = "abc.xyz";
 
     private static final OfflinePageItem TEST_OFFLINE_PAGE_ITEM = new OfflinePageItem(TEST_URL,
             TEST_OFFLINE_ID, TEST_NAMESPACE, TEST_ID, TEST_FILE_PATH, TEST_FILESIZE,
-            TEST_CREATIONTIMEMS, TEST_ACCESSCOUNT, TEST_LASTACCESSTIMEMS);
+            TEST_CREATIONTIMEMS, TEST_ACCESSCOUNT, TEST_LASTACCESSTIMEMS, TEST_REQUEST_ORIGIN);
 
     @Captor
     ArgumentCaptor<List<OfflinePageItem>> mResultArgument;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageOriginUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageOriginUnitTest.java
new file mode 100644
index 0000000..46dcf381
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageOriginUnitTest.java
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.offlinepages;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+/** Unit tests for {@link OfflinePageOrigin}. */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class OfflinePageOriginUnitTest {
+    @Test
+    public void testEncodeAsJson() {
+        String appName = "abc.xyz";
+        String[] signatures = new String[] {"deadbeef", "00c0ffee"};
+        OfflinePageOrigin origin = new OfflinePageOrigin(appName, signatures);
+
+        assertEquals("[\"abc.xyz\",[\"deadbeef\",\"00c0ffee\"]]", origin.encodeAsJsonString());
+    }
+}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index fca4630..5c94611 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1203,10 +1203,10 @@
      flag_descriptions::kGlCompositedOverlayCandidateQuadBordersName,
      flag_descriptions::kGlCompositedOverlayCandidateQuadBordersDescription,
      kOsAll,
-     SINGLE_VALUE_TYPE(cc::switches::kGlCompositedOverlayCandidateQuadBorder)},
+     SINGLE_VALUE_TYPE(switches::kGlCompositedOverlayCandidateQuadBorder)},
     {"show-overdraw-feedback", flag_descriptions::kShowOverdrawFeedbackName,
      flag_descriptions::kShowOverdrawFeedbackDescription, kOsAll,
-     SINGLE_VALUE_TYPE(cc::switches::kShowOverdrawFeedback)},
+     SINGLE_VALUE_TYPE(switches::kShowOverdrawFeedback)},
     {"ui-disable-partial-swap", flag_descriptions::kUiPartialSwapName,
      flag_descriptions::kUiPartialSwapDescription, kOsAll,
      SINGLE_DISABLE_VALUE_TYPE(switches::kUIDisablePartialSwap)},
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
index 17e3aa7..ca1a48c 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
@@ -30,9 +30,24 @@
 
 namespace {
 
-bool IsDisabledForProfile(Profile* profile) {
+// Whether auto opt out is enabled. Note that this does not disable collection
+// of data required for auto opt out.
+const bool kEnableAutoOptOutDefault = true;
+const char kEnableAutoOptOutParamName[] = "enable_auto_opt_out";
+
+bool IsAutoOptOutEnabled() {
+  return variations::GetVariationParamByFeatureAsBool(
+      ntp_snippets::kNotificationsFeature, kEnableAutoOptOutParamName,
+      kEnableAutoOptOutDefault);
+}
+
+bool IsEnabledForProfile(Profile* profile) {
   PrefService* prefs = profile->GetPrefs();
   if (!prefs->GetBoolean(prefs::kContentSuggestionsNotificationsEnabled)) {
+    return false;
+  }
+
+  if (!IsAutoOptOutEnabled()) {
     return true;
   }
 
@@ -41,7 +56,7 @@
   int limit = variations::GetVariationParamByFeatureAsInt(
       kNotificationsFeature, kNotificationsIgnoredLimitParam,
       kNotificationsIgnoredDefaultLimit);
-  return current >= limit;
+  return current < limit;
 }
 
 }  // namespace
@@ -100,9 +115,9 @@
   Java_ContentSuggestionsNotificationHelper_flushCachedMetrics(env);
 }
 
-bool ContentSuggestionsNotificationHelper::IsDisabledForProfile(
+bool ContentSuggestionsNotificationHelper::IsEnabledForProfile(
     Profile* profile) {
-  return ntp_snippets::IsDisabledForProfile(profile);
+  return ntp_snippets::IsEnabledForProfile(profile);
 }
 
 static void RecordNotificationOptOut(JNIEnv* env,
@@ -166,7 +181,7 @@
         CONTENT_SUGGESTIONS_HIDE_SHUTDOWN);
   }
 
-  const bool was_disabled = IsDisabledForProfile(profile);
+  const bool was_enabled = IsEnabledForProfile(profile);
   if (tap_count == 0) {
     // There were no taps, consecutive_ignored has not been reset and continues
     // from where it left off. If there was a tap, then Java has provided us
@@ -176,8 +191,8 @@
   }
   prefs->SetInteger(prefs::kContentSuggestionsConsecutiveIgnoredPrefName,
                     consecutive_ignored);
-  const bool is_disabled = IsDisabledForProfile(profile);
-  if (!was_disabled && is_disabled) {
+  const bool is_enabled = IsEnabledForProfile(profile);
+  if (was_enabled && !is_enabled) {
     RecordContentSuggestionsNotificationOptOut(CONTENT_SUGGESTIONS_IMPLICIT);
   }
 }
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.h b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
index be7b35c..a5d1695 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.h
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
@@ -45,9 +45,9 @@
   // is computed in turn from that.
   static void FlushCachedMetrics();
 
-  // True if the user has ignored enough notifications that we no longer think
-  // that the user is interested in them.
-  static bool IsDisabledForProfile(Profile* profile);
+  // False if auto opt out is enabled and the user has ignored enough
+  // notifications that we no longer think that the user is interested in them.
+  static bool IsEnabledForProfile(Profile* profile);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ContentSuggestionsNotificationHelper);
diff --git a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
index b970049a..40cbbb5f 100644
--- a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
@@ -121,7 +121,7 @@
     if (!ShouldNotifyInState(app_status_listener_.GetState())) {
       DVLOG(1) << "Suppressed notification because Chrome is frontmost";
       return;
-    } else if (ContentSuggestionsNotificationHelper::IsDisabledForProfile(
+    } else if (!ContentSuggestionsNotificationHelper::IsEnabledForProfile(
                    profile_)) {
       DVLOG(1) << "Suppressed notification due to opt-out";
       return;
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
index a896fc1c..0219a03 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
+++ b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
@@ -97,7 +97,8 @@
 
 void SavePageIfNotNavigatedAway(const GURL& url,
                                 const GURL& original_url,
-                                const ScopedJavaGlobalRef<jobject>& j_tab_ref) {
+                                const ScopedJavaGlobalRef<jobject>& j_tab_ref,
+                                const std::string& origin) {
   content::WebContents* web_contents = GetWebContentsFromJavaTab(j_tab_ref);
   if (!web_contents)
     return;
@@ -129,6 +130,7 @@
       params.availability =
           RequestCoordinator::RequestAvailability::DISABLED_FOR_OFFLINER;
       params.original_url = original_url;
+      params.request_origin = origin;
       request_id = request_coordinator->SavePageLater(params);
     } else {
       DVLOG(1) << "SavePageIfNotNavigatedAway has no valid coordinator.";
@@ -153,7 +155,7 @@
     }
     return;
   }
-  tab_helper->ObserveAndDownloadCurrentPage(client_id, request_id);
+  tab_helper->ObserveAndDownloadCurrentPage(client_id, request_id, origin);
 
   OfflinePageNotificationBridge notification_bridge;
   notification_bridge.ShowDownloadingToast();
@@ -162,9 +164,10 @@
 void DuplicateCheckDone(const GURL& url,
                         const GURL& original_url,
                         const ScopedJavaGlobalRef<jobject>& j_tab_ref,
+                        const std::string& origin,
                         OfflinePageUtils::DuplicateCheckResult result) {
   if (result == OfflinePageUtils::DuplicateCheckResult::NOT_FOUND) {
-    SavePageIfNotNavigatedAway(url, original_url, j_tab_ref);
+    SavePageIfNotNavigatedAway(url, original_url, j_tab_ref, origin);
     return;
   }
 
@@ -175,7 +178,8 @@
   bool duplicate_request_exists =
       result == OfflinePageUtils::DuplicateCheckResult::DUPLICATE_REQUEST_FOUND;
   OfflinePageInfoBarDelegate::Create(
-      base::Bind(&SavePageIfNotNavigatedAway, url, original_url, j_tab_ref),
+      base::Bind(&SavePageIfNotNavigatedAway, url, original_url, j_tab_ref,
+                 origin),
       url, duplicate_request_exists, web_contents);
 }
 
@@ -389,7 +393,8 @@
 void OfflinePageDownloadBridge::StartDownload(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& j_tab) {
+    const JavaParamRef<jobject>& j_tab,
+    const JavaParamRef<jstring>& j_origin) {
   TabAndroid* tab = TabAndroid::GetNativeTab(env, j_tab);
   if (!tab)
     return;
@@ -401,6 +406,7 @@
   GURL url = web_contents->GetLastCommittedURL();
   if (url.is_empty())
     return;
+  std::string origin = ConvertJavaStringToUTF8(env, j_origin);
 
   GURL original_url =
       offline_pages::OfflinePageUtils::GetOriginalURLFromWebContents(
@@ -421,7 +427,7 @@
 
   OfflinePageUtils::CheckDuplicateDownloads(
       tab->GetProfile()->GetOriginalProfile(), url,
-      base::Bind(&DuplicateCheckDone, url, original_url, j_tab_ref));
+      base::Bind(&DuplicateCheckDone, url, original_url, j_tab_ref, origin));
 }
 
 void OfflinePageDownloadBridge::CancelDownload(
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
index c5416da..6576fca 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
+++ b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
@@ -55,10 +55,10 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& j_guid);
 
-  void StartDownload(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_tab);
+  void StartDownload(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& obj,
+                     const base::android::JavaParamRef<jobject>& j_tab,
+                     const base::android::JavaParamRef<jstring>& j_origin);
 
   void CancelDownload(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
index 6ec53df5..27e429b 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
+++ b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
@@ -65,7 +65,8 @@
         ConvertUTF8ToJavaString(env, offline_page.client_id.id),
         ConvertUTF8ToJavaString(env, offline_page.file_path.value()),
         offline_page.file_size, offline_page.creation_time.ToJavaTime(),
-        offline_page.access_count, offline_page.last_access_time.ToJavaTime());
+        offline_page.access_count, offline_page.last_access_time.ToJavaTime(),
+        ConvertUTF8ToJavaString(env, offline_page.request_origin));
   }
 }
 
diff --git a/chrome/browser/extensions/error_console/error_console_browsertest.cc b/chrome/browser/extensions/error_console/error_console_browsertest.cc
index b86771f..c8efca97 100644
--- a/chrome/browser/extensions/error_console/error_console_browsertest.cc
+++ b/chrome/browser/extensions/error_console/error_console_browsertest.cc
@@ -450,10 +450,12 @@
   std::string message;
   bool use_native_bindings = FeatureSwitch::native_crx_bindings()->IsEnabled();
   if (use_native_bindings) {
-    // TODO(devlin): The "Error in event handler for browserAction.onClicked"
-    // portion may or may not be worth preserving. In most cases, it's
-    // unnecessary with the line number, but it could be useful in some cases.
-    message = "Uncaught ReferenceError: baz is not defined";
+    // TODO(devlin): The specific event name (here, 'browserAction.onClicked')
+    // may or may not be worth preserving. In most cases, it's unnecessary with
+    // the line number, but it could be useful in some cases.
+    message =
+        "Error in event handler: ReferenceError: "
+        "baz is not defined";
   } else {
     message =
         "Error in event handler for browserAction.onClicked: "
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 38d0e1af..2047073 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -64,7 +64,8 @@
         ConvertUTF8ToJavaString(env, offline_page.client_id.id),
         ConvertUTF8ToJavaString(env, offline_page.file_path.value()),
         offline_page.file_size, offline_page.creation_time.ToJavaTime(),
-        offline_page.access_count, offline_page.last_access_time.ToJavaTime());
+        offline_page.access_count, offline_page.last_access_time.ToJavaTime(),
+        ConvertUTF8ToJavaString(env, offline_page.request_origin));
   }
 }
 
@@ -78,7 +79,8 @@
       ConvertUTF8ToJavaString(env, offline_page.client_id.id),
       ConvertUTF8ToJavaString(env, offline_page.file_path.value()),
       offline_page.file_size, offline_page.creation_time.ToJavaTime(),
-      offline_page.access_count, offline_page.last_access_time.ToJavaTime());
+      offline_page.access_count, offline_page.last_access_time.ToJavaTime(),
+      ConvertUTF8ToJavaString(env, offline_page.request_origin));
 }
 
 ScopedJavaLocalRef<jobject> ToJavaDeletedPageInfo(
@@ -676,13 +678,15 @@
     const base::android::JavaParamRef<jobject>& j_web_contents,
     const JavaParamRef<jstring>& j_namespace,
     const JavaParamRef<jstring>& j_url,
-    int ui_action) {
+    int ui_action,
+    const JavaParamRef<jstring>& j_origin) {
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(j_web_contents);
   OfflinePageUtils::ScheduleDownload(
       web_contents, ConvertJavaStringToUTF8(env, j_namespace),
       GURL(ConvertJavaStringToUTF8(env, j_url)),
-      static_cast<OfflinePageUtils::DownloadUIActionFlags>(ui_action));
+      static_cast<OfflinePageUtils::DownloadUIActionFlags>(ui_action),
+      ConvertJavaStringToUTF8(env, j_origin));
 }
 
 jboolean OfflinePageBridge::IsOfflinePage(
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.h b/chrome/browser/offline_pages/android/offline_page_bridge.h
index c802f69..a23497d4 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.h
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.h
@@ -153,7 +153,8 @@
       const base::android::JavaParamRef<jobject>& j_web_contents,
       const base::android::JavaParamRef<jstring>& j_namespace,
       const base::android::JavaParamRef<jstring>& j_url,
-      int ui_action);
+      int ui_action,
+      const base::android::JavaParamRef<jstring>& j_origin);
 
   base::android::ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; }
 
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.cc b/chrome/browser/offline_pages/offline_page_tab_helper.cc
index 99f48640f..06420a5e 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.cc
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.cc
@@ -223,12 +223,13 @@
     content::WebContents* web_contents,
     const std::string& name_space,
     const GURL& url,
-    OfflinePageUtils::DownloadUIActionFlags ui_action) {
+    OfflinePageUtils::DownloadUIActionFlags ui_action,
+    const std::string& request_origin) {
   OfflinePageUtils::CheckDuplicateDownloads(
       web_contents->GetBrowserContext(), url,
       base::Bind(&OfflinePageTabHelper::DuplicateCheckDoneForScheduleDownload,
                  weak_ptr_factory_.GetWeakPtr(), web_contents, name_space, url,
-                 ui_action));
+                 ui_action, request_origin));
 }
 
 void OfflinePageTabHelper::DuplicateCheckDoneForScheduleDownload(
@@ -236,6 +237,7 @@
     const std::string& name_space,
     const GURL& url,
     OfflinePageUtils::DownloadUIActionFlags ui_action,
+    const std::string& request_origin,
     OfflinePageUtils::DuplicateCheckResult result) {
   if (result != OfflinePageUtils::DuplicateCheckResult::NOT_FOUND) {
     if (static_cast<int>(ui_action) &
@@ -244,7 +246,7 @@
       OfflinePageUtils::ShowDuplicatePrompt(
           base::Bind(&OfflinePageTabHelper::DoDownloadPageLater,
                      weak_ptr_factory_.GetWeakPtr(), web_contents, name_space,
-                     url, ui_action),
+                     url, ui_action, request_origin),
           url,
           result ==
               OfflinePageUtils::DuplicateCheckResult::DUPLICATE_REQUEST_FOUND,
@@ -253,14 +255,15 @@
     }
   }
 
-  DoDownloadPageLater(web_contents, name_space, url, ui_action);
+  DoDownloadPageLater(web_contents, name_space, url, ui_action, request_origin);
 }
 
 void OfflinePageTabHelper::DoDownloadPageLater(
     content::WebContents* web_contents,
     const std::string& name_space,
     const GURL& url,
-    OfflinePageUtils::DownloadUIActionFlags ui_action) {
+    OfflinePageUtils::DownloadUIActionFlags ui_action,
+    const std::string& request_origin) {
   offline_pages::RequestCoordinator* request_coordinator =
       offline_pages::RequestCoordinatorFactory::GetForBrowserContext(
           web_contents->GetBrowserContext());
@@ -270,6 +273,7 @@
   offline_pages::RequestCoordinator::SavePageLaterParams params;
   params.url = url;
   params.client_id = offline_pages::ClientId(name_space, base::GenerateGUID());
+  params.request_origin = request_origin;
   request_coordinator->SavePageLater(params);
 
   if (static_cast<int>(ui_action) &
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.h b/chrome/browser/offline_pages/offline_page_tab_helper.h
index 66757cd..dd6e662 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.h
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.h
@@ -57,11 +57,11 @@
   // OfflinePageTabHelper instance is tied with the associated |web_contents|
   // and thus the callback will be automatically invalidated if |web_contents|
   // is gone.
-  void ScheduleDownloadHelper(
-      content::WebContents* web_contents,
-      const std::string& name_space,
-      const GURL& url,
-      OfflinePageUtils::DownloadUIActionFlags ui_action);
+  void ScheduleDownloadHelper(content::WebContents* web_contents,
+                              const std::string& name_space,
+                              const GURL& url,
+                              OfflinePageUtils::DownloadUIActionFlags ui_action,
+                              const std::string& request_origin);
 
  private:
   friend class content::WebContentsUserData<OfflinePageTabHelper>;
@@ -99,11 +99,13 @@
       const std::string& name_space,
       const GURL& url,
       OfflinePageUtils::DownloadUIActionFlags ui_action,
+      const std::string& request_origin,
       OfflinePageUtils::DuplicateCheckResult result);
   void DoDownloadPageLater(content::WebContents* web_contents,
                            const std::string& name_space,
                            const GURL& url,
-                           OfflinePageUtils::DownloadUIActionFlags ui_action);
+                           OfflinePageUtils::DownloadUIActionFlags ui_action,
+                           const std::string& request_origin);
 
   // The provisional info about the offline page being loaded. This is set when
   // the offline interceptor decides to serve the offline page and it will be
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc
index b847891d..c86158d 100644
--- a/chrome/browser/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -283,14 +283,24 @@
 void OfflinePageUtils::ScheduleDownload(content::WebContents* web_contents,
                                         const std::string& name_space,
                                         const GURL& url,
-                                        DownloadUIActionFlags ui_action) {
+                                        DownloadUIActionFlags ui_action,
+                                        const std::string& request_origin) {
   DCHECK(web_contents);
 
   OfflinePageTabHelper* tab_helper =
       OfflinePageTabHelper::FromWebContents(web_contents);
   if (!tab_helper)
     return;
-  tab_helper->ScheduleDownloadHelper(web_contents, name_space, url, ui_action);
+  tab_helper->ScheduleDownloadHelper(web_contents, name_space, url, ui_action,
+                                     request_origin);
+}
+
+// static
+void OfflinePageUtils::ScheduleDownload(content::WebContents* web_contents,
+                                        const std::string& name_space,
+                                        const GURL& url,
+                                        DownloadUIActionFlags ui_action) {
+  ScheduleDownload(web_contents, name_space, url, ui_action, "");
 }
 
 // static
diff --git a/chrome/browser/offline_pages/offline_page_utils.h b/chrome/browser/offline_pages/offline_page_utils.h
index e35769b3..1d6dc7a 100644
--- a/chrome/browser/offline_pages/offline_page_utils.h
+++ b/chrome/browser/offline_pages/offline_page_utils.h
@@ -127,6 +127,12 @@
   static void ScheduleDownload(content::WebContents* web_contents,
                                const std::string& name_space,
                                const GURL& url,
+                               DownloadUIActionFlags ui_action,
+                               const std::string& request_origin);
+
+  static void ScheduleDownload(content::WebContents* web_contents,
+                               const std::string& name_space,
+                               const GURL& url,
                                DownloadUIActionFlags ui_action);
 
   // Determines if offline page download should be triggered based on MIME type
diff --git a/chrome/browser/offline_pages/recent_tab_helper.cc b/chrome/browser/offline_pages/recent_tab_helper.cc
index e036808e..1473a65 100644
--- a/chrome/browser/offline_pages/recent_tab_helper.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper.cc
@@ -67,8 +67,10 @@
 struct RecentTabHelper::SnapshotProgressInfo {
  public:
   // For a downloads snapshot request, where the |request_id| is defined.
-  SnapshotProgressInfo(const ClientId& client_id, int64_t request_id)
-      : client_id(client_id), request_id(request_id) {}
+  SnapshotProgressInfo(const ClientId& client_id,
+                       int64_t request_id,
+                       const std::string& origin)
+      : client_id(client_id), request_id(request_id), origin(origin) {}
 
   // For a last_n snapshot request.
   explicit SnapshotProgressInfo(const ClientId& client_id)
@@ -89,6 +91,10 @@
   // valid for successfully saved snapshots.
   SnapshotController::PageQuality expected_page_quality =
       SnapshotController::PageQuality::POOR;
+
+  // The app that created the tab - either a package name of the CCT origin
+  // or empty, meaning chrome.
+  std::string origin = "";
 };
 
 RecentTabHelper::RecentTabHelper(content::WebContents* web_contents)
@@ -107,13 +113,14 @@
   delegate_ = std::move(delegate);
 }
 
-void RecentTabHelper::ObserveAndDownloadCurrentPage(
-    const ClientId& client_id, int64_t request_id) {
+void RecentTabHelper::ObserveAndDownloadCurrentPage(const ClientId& client_id,
+                                                    int64_t request_id,
+                                                    const std::string& origin) {
   // Note: as this implementation only supports one client namespace, enforce
   // that the call is from Downloads.
   DCHECK_EQ(kDownloadNamespace, client_id.name_space);
   auto new_downloads_snapshot_info =
-      base::MakeUnique<SnapshotProgressInfo>(client_id, request_id);
+      base::MakeUnique<SnapshotProgressInfo>(client_id, request_id, origin);
 
   // If this tab helper is not enabled, immediately give the job back to
   // RequestCoordinator.
@@ -388,7 +395,8 @@
     DCHECK(!downloads_ongoing_snapshot_info_);
     downloads_ongoing_snapshot_info_ = base::MakeUnique<SnapshotProgressInfo>(
         downloads_latest_saved_snapshot_info_->client_id,
-        downloads_latest_saved_snapshot_info_->request_id);
+        downloads_latest_saved_snapshot_info_->request_id,
+        downloads_latest_saved_snapshot_info_->origin);
     std::vector<int64_t> ids{downloads_latest_saved_snapshot_info_->request_id};
     ContinueSnapshotWithIdsToPurge(downloads_ongoing_snapshot_info_.get(), ids);
   } else {
@@ -439,6 +447,7 @@
   save_page_params.is_background = false;
   save_page_params.original_url =
       OfflinePageUtils::GetOriginalURLFromWebContents(web_contents());
+  save_page_params.request_origin = snapshot_info->origin;
   page_model_->SavePage(
       save_page_params, delegate_->CreatePageArchiver(web_contents()),
       base::Bind(&RecentTabHelper::SavePageCallback,
diff --git a/chrome/browser/offline_pages/recent_tab_helper.h b/chrome/browser/offline_pages/recent_tab_helper.h
index 63bdeb4..a211f81 100644
--- a/chrome/browser/offline_pages/recent_tab_helper.h
+++ b/chrome/browser/offline_pages/recent_tab_helper.h
@@ -87,7 +87,8 @@
   // Note #2: Currently this method only accepts download requests from the
   // downloads namespace.
   void ObserveAndDownloadCurrentPage(const ClientId& client_id,
-                                     int64_t request_id);
+                                     int64_t request_id,
+                                     const std::string& origin);
 
  private:
   struct SnapshotProgressInfo;
diff --git a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
index 5967261..f7077d5 100644
--- a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
@@ -342,7 +342,7 @@
   FastForwardSnapshotController();
   recent_tab_helper()->WasHidden();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     123L);
+                                                     123L, "");
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
   // No page should be captured.
@@ -368,7 +368,7 @@
 
   // But the following download request should work normally
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     123L);
+                                                     123L, "");
   RunUntilIdle();
   EXPECT_EQ(1U, page_added_count());
   ASSERT_EQ(1U, GetAllPages().size());
@@ -392,7 +392,7 @@
 
   // But the following download request should work normally
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     123L);
+                                                     123L, "");
   RunUntilIdle();
   EXPECT_EQ(1U, page_added_count());
   ASSERT_EQ(1U, GetAllPages().size());
@@ -608,7 +608,7 @@
   const int64_t second_offline_id = first_offline_id + 1;
   const ClientId second_client_id = NewDownloadClientId();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(second_client_id,
-                                                     second_offline_id);
+                                                     second_offline_id, "");
   RunUntilIdle();
   EXPECT_EQ(3U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
@@ -623,7 +623,7 @@
   const int64_t third_offline_id = first_offline_id + 2;
   const ClientId third_client_id = NewDownloadClientId();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(third_client_id,
-                                                     third_offline_id);
+                                                     third_offline_id, "");
   RunUntilIdle();
   EXPECT_EQ(4U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
@@ -644,7 +644,7 @@
   FastForwardSnapshotController();
   recent_tab_helper()->WasHidden();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     123L);
+                                                     123L, "");
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
   ASSERT_EQ(0U, GetAllPages().size());
@@ -665,7 +665,7 @@
   ASSERT_EQ(0U, GetAllPages().size());
 
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     123L);
+                                                     123L, "");
   RunUntilIdle();
   // No page should be captured.
   ASSERT_EQ(1U, GetAllPages().size());
@@ -678,7 +678,7 @@
   // so far.
   NavigateAndCommit(kTestPageUrl);
   const ClientId client_id = NewDownloadClientId();
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L, "");
   FastForwardSnapshotController();
   EXPECT_TRUE(model()->is_loaded());
   ASSERT_EQ(0U, GetAllPages().size());
@@ -715,7 +715,7 @@
   ASSERT_EQ(0U, GetAllPages().size());
 
   const ClientId client_id = NewDownloadClientId();
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L, "");
   RunUntilIdle();
   ASSERT_EQ(1U, GetAllPages().size());
   const OfflinePageItem& page = GetAllPages()[0];
@@ -740,13 +740,34 @@
   ASSERT_EQ(0U, GetAllPages().size());
 
   const ClientId client_id = NewDownloadClientId();
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L, "");
   RunUntilIdle();
   ASSERT_EQ(1U, GetAllPages().size());
   const OfflinePageItem& page = GetAllPages()[0];
   EXPECT_EQ(kTestPageUrl, page.url);
   EXPECT_EQ(client_id, page.client_id);
   EXPECT_EQ(153L, page.offline_id);
+  EXPECT_EQ("", page.request_origin);
+}
+
+// Simulates a download request to offline the current page made after loading
+// is completed. Should end up with one offline page.
+TEST_F(RecentTabHelperTest, DownloadRequestAfterFullyLoadWithOrigin) {
+  NavigateAndCommit(kTestPageUrl);
+  recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
+  FastForwardSnapshotController();
+  EXPECT_TRUE(model()->is_loaded());
+  ASSERT_EQ(0U, GetAllPages().size());
+
+  const ClientId client_id = NewDownloadClientId();
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, 153L, "abc");
+  RunUntilIdle();
+  ASSERT_EQ(1U, GetAllPages().size());
+  const OfflinePageItem& page = GetAllPages()[0];
+  EXPECT_EQ(kTestPageUrl, page.url);
+  EXPECT_EQ(client_id, page.client_id);
+  EXPECT_EQ(153L, page.offline_id);
+  EXPECT_EQ("abc", page.request_origin);
 }
 
 // Simulates requests coming from last_n and downloads at the same time for a
@@ -759,7 +780,7 @@
   const int64_t download_offline_id = 153L;
   const ClientId download_client_id = NewDownloadClientId();
   recent_tab_helper()->ObserveAndDownloadCurrentPage(download_client_id,
-                                                     download_offline_id);
+                                                     download_offline_id, "");
   RunUntilIdle();
   ASSERT_EQ(2U, GetAllPages().size());
 
@@ -818,9 +839,10 @@
   NavigateAndCommit(kTestPageUrl);
   const ClientId client_id_1 = NewDownloadClientId();
   const int64_t offline_id_1 = 153L;
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id_1, offline_id_1);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id_1, offline_id_1,
+                                                     "");
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     351L);
+                                                     351L, "");
 
   // Finish loading the page. Only the first request should be executed.
   recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
@@ -836,9 +858,10 @@
   // generate a snapshot.
   const ClientId client_id_3 = NewDownloadClientId();
   const int64_t offline_id_3 = 789L;
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id_3, offline_id_3);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id_3, offline_id_3,
+                                                     "");
   recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
-                                                     987L);
+                                                     987L, "");
   RunUntilIdle();
   EXPECT_EQ(2U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
@@ -876,7 +899,7 @@
   // Now create a download request and check the snapshot is properly created.
   const ClientId client_id = NewDownloadClientId();
   const int64_t offline_id = 153L;
-  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, offline_id);
+  recent_tab_helper()->ObserveAndDownloadCurrentPage(client_id, offline_id, "");
   RunUntilIdle();
   EXPECT_EQ(3U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 9695eda..f7c3a6bd 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -218,11 +218,34 @@
 
 
 /**
+ * Updates the OneGoogleBar (if it is loaded) based on the current theme.
+ * @private
+ */
+function renderOneGoogleBarTheme() {
+  if (!window.gbar) {
+    return;
+  }
+  try {
+    var oneGoogleBarApi = window.gbar.a;
+    var oneGoogleBarPromise = oneGoogleBarApi.bf();
+    oneGoogleBarPromise.then(function(oneGoogleBar) {
+      var isThemeDark = getIsThemeDark(ntpApiHandle.themeBackgroundInfo);
+      var setForegroundStyle = oneGoogleBar.pc.bind(oneGoogleBar);
+      setForegroundStyle(isThemeDark ? 1 : 0);
+    });
+  } catch (err) {
+    console.log('Failed setting OneGoogleBar theme:\n' + err);
+  }
+}
+
+
+/**
  * Callback for embeddedSearch.newTabPage.onthemechange.
  * @private
  */
 function onThemeChange() {
   renderTheme();
+  renderOneGoogleBarTheme();
 }
 
 
@@ -685,6 +708,8 @@
   inHeadScript.appendChild(document.createTextNode(ogb.inHeadScript));
   document.head.appendChild(inHeadScript);
 
+  renderOneGoogleBarTheme();
+
   var ogElem = $('one-google');
   ogElem.innerHTML = ogb.barHtml;
   ogElem.classList.remove('hidden');
diff --git a/chrome/browser/resources/offline_pages/offline_internals.html b/chrome/browser/resources/offline_pages/offline_internals.html
index 7220e63a..6aae579 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.html
+++ b/chrome/browser/resources/offline_pages/offline_internals.html
@@ -56,10 +56,21 @@
           <th>Namespace</th>
           <th>Size (Kb)</th>
           <th>Expired</th>
+          <th>Request Origin</th>
         </tr>
       </thead>
       <tbody id="stored-pages"> </tbody>
     </table>
+    <template id="stored-pages-table-row">
+      <tr>
+        <td><input type="checkbox" name="stored"></td>
+        <td><a></a></td>
+        <td></td>
+        <td></td>
+        <td></td>
+        <td></td>
+      </tr>
+    </template>
     <div id="page-actions-info" class="dump"></div>
 
     <h2>Request Queue</h2>
@@ -74,10 +85,20 @@
           <th>URL</th>
           <th>Created Timestamp</th>
           <th>Status</th>
+          <th>Request Origin</th>
         </tr>
       </thead>
       <tbody id="request-queue"> </tbody>
     </table>
+    <template id="request-queue-table-row">
+      <tr>
+        <td><input type="checkbox" name="requests"></td>
+        <td></td>
+        <td></td>
+        <td></td>
+        <td></td>
+      </tr>
+    </template>
     <div id="request-queue-actions-info" class="dump"></div>
     <input id="url" type="url"
         placeholder="http://www.url1.com, http://www.url2.com, ...">
diff --git a/chrome/browser/resources/offline_pages/offline_internals.js b/chrome/browser/resources/offline_pages/offline_internals.js
index 5acddb5..dc589404 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.js
+++ b/chrome/browser/resources/offline_pages/offline_internals.js
@@ -33,37 +33,22 @@
     var storedPagesTable = $('stored-pages');
     storedPagesTable.textContent = '';
 
+    var template = $('stored-pages-table-row');
+    var td = template.content.querySelectorAll('td');
     for (var i = 0; i < pages.length; i++) {
-      var row = document.createElement('tr');
-
-      var checkboxCell = document.createElement('td');
-      var checkbox = document.createElement('input');
-      checkbox.setAttribute('type', 'checkbox');
-      checkbox.setAttribute('name', 'stored');
+      var checkbox = td[0].querySelector('input');
       checkbox.setAttribute('value', pages[i].id);
 
-      checkboxCell.appendChild(checkbox);
-      row.appendChild(checkboxCell);
-
-      var cell = document.createElement('td');
-      var link = document.createElement('a');
+      var link = td[1].querySelector('a');
       link.setAttribute('href', pages[i].onlineUrl);
       link.textContent = pages[i].onlineUrl;
-      cell.appendChild(link);
-      row.appendChild(cell);
 
-      cell = document.createElement('td');
-      cell.textContent = pages[i].namespace;
-      row.appendChild(cell);
+      td[2].textContent = pages[i].namespace;
+      td[3].textContent = Math.round(pages[i].size / 1024);
+      td[4].textContent = pages[i].isExpired;
+      td[5].textContent = pages[i].requestOrigin;
 
-      cell = document.createElement('td');
-      cell.textContent = Math.round(pages[i].size / 1024);
-      row.appendChild(cell);
-
-      cell = document.createElement('td');
-      cell.textContent = pages[i].isExpired;
-      row.appendChild(cell);
-
+      var row = document.importNode(template.content, true);
       storedPagesTable.appendChild(row);
     }
     offlinePages = pages;
@@ -78,30 +63,18 @@
     var requestQueueTable = $('request-queue');
     requestQueueTable.textContent = '';
 
+    var template = $('request-queue-table-row');
+    var td = template.content.querySelectorAll('td');
     for (var i = 0; i < requests.length; i++) {
-      var row = document.createElement('tr');
-
-      var checkboxCell = document.createElement('td');
-      var checkbox = document.createElement('input');
-      checkbox.setAttribute('type', 'checkbox');
-      checkbox.setAttribute('name', 'requests');
+      var checkbox = td[0].querySelector('input');
       checkbox.setAttribute('value', requests[i].id);
 
-      checkboxCell.appendChild(checkbox);
-      row.appendChild(checkboxCell);
+      td[1].textContent = requests[i].onlineUrl;
+      td[2].textContent = new Date(requests[i].creationTime);
+      td[3].textContent = requests[i].status;
+      td[4].textContent = requests[i].requestOrigin;
 
-      var cell = document.createElement('td');
-      cell.textContent = requests[i].onlineUrl;
-      row.appendChild(cell);
-
-      cell = document.createElement('td');
-      cell.textContent = new Date(requests[i].creationTime);
-      row.appendChild(cell);
-
-      cell = document.createElement('td');
-      cell.textContent = requests[i].status;
-      row.appendChild(cell);
-
+      var row = document.importNode(template.content, true);
       requestQueueTable.appendChild(row);
     }
     savePageRequests = requests;
diff --git a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
index 81d58c11..14777fc 100644
--- a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
+++ b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
@@ -12,7 +12,8 @@
  *   filePath: string,
  *   lastAccessTime: number,
  *   accessCount: number,
- *   isExpired: string
+ *   isExpired: string,
+ *   requestOrigin: string
  * }}
  */
 var OfflinePage;
@@ -24,7 +25,8 @@
  *   creationTime: number,
  *   id: string,
  *   namespace: string,
- *   lastAttempt: number
+ *   lastAttempt: number,
+ *   requestOrigin: string
  * }}
  */
 var SavePageRequest;
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index b84ce84..768e980 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -224,8 +224,7 @@
       DCHECK(!use_new_window);
       web_contents = chrome::ReplaceRestoredTab(
           browser, tab.navigations, selected_index, true, tab.extension_app_id,
-          nullptr, tab.user_agent_override);
-      SessionRestore::OnWillRestoreTab(web_contents);
+          nullptr, tab.user_agent_override, true /* from_session_restore */);
     } else {
       int tab_index =
           use_new_window ? 0 : browser->tab_strip_model()->active_index() + 1;
@@ -233,8 +232,8 @@
           browser, tab.navigations, tab_index, selected_index,
           tab.extension_app_id,
           disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB,  // selected
-          tab.pinned, true, nullptr, tab.user_agent_override);
-      SessionRestore::OnWillRestoreTab(web_contents);
+          tab.pinned, true, nullptr, tab.user_agent_override,
+          true /* from_session_restore */);
       // Start loading the tab immediately.
       web_contents->GetController().LoadIfNecessary();
     }
@@ -596,7 +595,8 @@
     WebContents* web_contents = chrome::AddRestoredTab(
         browser, tab.navigations, tab_index, selected_index,
         tab.extension_app_id, is_selected_tab, tab.pinned, true,
-        session_storage_namespace.get(), tab.user_agent_override);
+        session_storage_namespace.get(), tab.user_agent_override,
+        true /* from_session_restore */);
     // Regression check: if the current tab |is_selected_tab|, it should load
     // immediately, otherwise, tabs should not start loading right away. The
     // focused tab will be loaded by Browser, and TabLoader will load the rest.
@@ -606,8 +606,6 @@
     if (!web_contents)
       return;
 
-    SessionRestore::OnWillRestoreTab(web_contents);
-
     // Sanitize the last active time.
     base::TimeDelta delta = highest_time - tab.last_active_time;
     web_contents->SetLastActiveTime(now - delta);
diff --git a/chrome/browser/sessions/session_restore.h b/chrome/browser/sessions/session_restore.h
index 77fa7c0a..e2310e1a 100644
--- a/chrome/browser/sessions/session_restore.h
+++ b/chrome/browser/sessions/session_restore.h
@@ -113,6 +113,9 @@
   // without session restore started.
   static void OnTabLoaderFinishedLoadingTabs();
 
+  // Is called when session restore is going to restore a tab.
+  static void OnWillRestoreTab(content::WebContents* web_contents);
+
  private:
   friend class SessionRestoreImpl;
   FRIEND_TEST_ALL_PREFIXES(SessionRestoreObserverTest, SingleSessionRestore);
@@ -152,9 +155,6 @@
   // the first session restore.
   static void NotifySessionRestoreStartedLoadingTabs();
 
-  // Is called when session restore is going to restore a tab.
-  static void OnWillRestoreTab(content::WebContents* web_contents);
-
   // Contains all registered observers for session restore events.
   static SessionRestoreObserverList* observers_;
 
diff --git a/chrome/browser/sessions/session_restore_observer_browsertest.cc b/chrome/browser/sessions/session_restore_observer_browsertest.cc
new file mode 100644
index 0000000..e9af8fc3
--- /dev/null
+++ b/chrome/browser/sessions/session_restore_observer_browsertest.cc
@@ -0,0 +1,241 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sessions/session_restore_observer.h"
+
+#include <memory>
+#include <unordered_map>
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/keep_alive_types.h"
+#include "chrome/browser/lifetime/scoped_keep_alive.h"
+#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sessions/session_service_factory.h"
+#include "chrome/browser/sessions/session_service_test_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::WebContents;
+using content::NavigationHandle;
+
+// This class records session-restore states of a tab when it starts navigation.
+class NavigationStartWebContentsObserver : public content::WebContentsObserver {
+ public:
+  explicit NavigationStartWebContentsObserver(WebContents* contents)
+      : WebContentsObserver(contents) {}
+
+  // content::WebContentsObserver implementation:
+  void DidStartNavigation(NavigationHandle* navigation_handle) override {
+    WebContents* contents = navigation_handle->GetWebContents();
+    resource_coordinator::TabManager* tab_manager =
+        g_browser_process->GetTabManager();
+    ASSERT_TRUE(tab_manager);
+
+    is_session_restored_ = tab_manager->IsTabInSessionRestore(contents);
+    is_restored_in_foreground_ =
+        tab_manager->IsTabRestoredInForeground(contents);
+  }
+
+  // Returns the session-restore states at the navigation start.
+  bool is_session_restored() const { return is_session_restored_; }
+  bool is_restored_in_foreground() const { return is_restored_in_foreground_; }
+
+ private:
+  bool is_session_restored_ = false;
+  bool is_restored_in_foreground_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationStartWebContentsObserver);
+};
+
+class MockSessionRestoreObserver : public SessionRestoreObserver {
+ public:
+  MockSessionRestoreObserver() { SessionRestore::AddObserver(this); }
+  ~MockSessionRestoreObserver() { SessionRestore::RemoveObserver(this); }
+
+  enum class SessionRestoreEvent { kStartedLoadingTabs, kFinishedLoadingTabs };
+
+  const std::vector<SessionRestoreEvent>& session_restore_events() const {
+    return session_restore_events_;
+  }
+
+  // SessionRestoreObserver implementation:
+  void OnSessionRestoreStartedLoadingTabs() override {
+    session_restore_events_.emplace_back(
+        SessionRestoreEvent::kStartedLoadingTabs);
+  }
+  void OnSessionRestoreFinishedLoadingTabs() override {
+    session_restore_events_.emplace_back(
+        SessionRestoreEvent::kFinishedLoadingTabs);
+  }
+  void OnWillRestoreTab(WebContents* contents) override {
+    navigation_start_observers_.emplace(
+        contents, new NavigationStartWebContentsObserver(contents));
+  }
+
+  NavigationStartWebContentsObserver*
+  GetNavigationStartWebContentsObserverForTab(WebContents* contents) {
+    return navigation_start_observers_[contents].get();
+  }
+
+ private:
+  std::vector<SessionRestoreEvent> session_restore_events_;
+  std::unordered_map<WebContents*,
+                     std::unique_ptr<NavigationStartWebContentsObserver>>
+      navigation_start_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSessionRestoreObserver);
+};
+
+class SessionRestoreObserverTest : public InProcessBrowserTest {
+ protected:
+  SessionRestoreObserverTest() {}
+
+  void SetUpOnMainThread() override {
+    SessionStartupPref pref(SessionStartupPref::LAST);
+    SessionStartupPref::SetStartupPref(browser()->profile(), pref);
+#if defined(OS_CHROMEOS)
+    SessionServiceTestHelper helper(
+        SessionServiceFactory::GetForProfile(browser()->profile()));
+    helper.SetForceBrowserNotAliveWithNoWindows(true);
+    helper.ReleaseService();
+#endif
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  Browser* QuitBrowserAndRestore(Browser* browser) {
+    Profile* profile = browser->profile();
+
+    std::unique_ptr<ScopedKeepAlive> keep_alive(new ScopedKeepAlive(
+        KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED));
+    CloseBrowserSynchronously(browser);
+
+    // Create a new window, which should trigger session restore.
+    chrome::NewEmptyWindow(profile);
+    ui_test_utils::BrowserAddedObserver window_observer;
+    return window_observer.WaitForSingleNewBrowser();
+  }
+
+  void WaitForTabsToLoad(Browser* browser) {
+    for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
+      WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(i);
+      contents->GetController().LoadIfNecessary();
+      ASSERT_TRUE(content::WaitForLoadStop(contents));
+    }
+  }
+
+  GURL GetTestURL() const {
+    return embedded_test_server()->GetURL("/title1.html");
+  }
+
+  const std::vector<MockSessionRestoreObserver::SessionRestoreEvent>&
+  session_restore_events() const {
+    return mock_observer_.session_restore_events();
+  }
+
+  size_t number_of_session_restore_events() const {
+    return session_restore_events().size();
+  }
+
+  MockSessionRestoreObserver& session_restore_observer() {
+    return mock_observer_;
+  }
+
+ private:
+  MockSessionRestoreObserver mock_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionRestoreObserverTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SessionRestoreObserverTest, SingleTabSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+  Browser* new_browser = QuitBrowserAndRestore(browser());
+
+  // The restored browser should have 1 tab.
+  TabStripModel* tab_strip = new_browser->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+  ASSERT_EQ(1, tab_strip->count());
+
+  ASSERT_EQ(1u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::kStartedLoadingTabs,
+      session_restore_events()[0]);
+
+  ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
+  ASSERT_EQ(2u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::kFinishedLoadingTabs,
+      session_restore_events()[1]);
+
+  // The only restored tab should be in foreground.
+  NavigationStartWebContentsObserver* observer =
+      session_restore_observer().GetNavigationStartWebContentsObserverForTab(
+          new_browser->tab_strip_model()->GetWebContentsAt(0));
+  EXPECT_TRUE(observer->is_session_restored());
+  EXPECT_TRUE(observer->is_restored_in_foreground());
+
+  // A new foreground tab should not be created by session restore.
+  ui_test_utils::NavigateToURLWithDisposition(
+      new_browser, GetTestURL(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  resource_coordinator::TabManager* tab_manager =
+      g_browser_process->GetTabManager();
+  WebContents* contents = new_browser->tab_strip_model()->GetWebContentsAt(1);
+  ASSERT_TRUE(contents);
+  EXPECT_FALSE(tab_manager->IsTabInSessionRestore(contents));
+  EXPECT_FALSE(tab_manager->IsTabRestoredInForeground(contents));
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestoreObserverTest, MultipleTabSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  Browser* new_browser = QuitBrowserAndRestore(browser());
+
+  // The restored browser should have 2 tabs.
+  TabStripModel* tab_strip = new_browser->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+  ASSERT_EQ(2, tab_strip->count());
+
+  ASSERT_EQ(1u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::kStartedLoadingTabs,
+      session_restore_events()[0]);
+
+  ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
+  ASSERT_EQ(2u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::kFinishedLoadingTabs,
+      session_restore_events()[1]);
+
+  // The first tab should be restored in foreground.
+  NavigationStartWebContentsObserver* observer =
+      session_restore_observer().GetNavigationStartWebContentsObserverForTab(
+          new_browser->tab_strip_model()->GetWebContentsAt(0));
+  ASSERT_TRUE(observer);
+  EXPECT_TRUE(observer->is_session_restored());
+  EXPECT_TRUE(observer->is_restored_in_foreground());
+
+  // The second tab should be restored in background.
+  observer =
+      session_restore_observer().GetNavigationStartWebContentsObserverForTab(
+          new_browser->tab_strip_model()->GetWebContentsAt(1));
+  ASSERT_TRUE(observer);
+  EXPECT_TRUE(observer->is_session_restored());
+  EXPECT_FALSE(observer->is_restored_in_foreground());
+}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d76ff2b..a68303b3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2761,8 +2761,6 @@
         "cocoa/content_settings/cookies_tree_controller_bridge.h",
         "cocoa/content_settings/cookies_tree_controller_bridge.mm",
         "cocoa/create_native_web_modal_manager_cocoa.mm",
-        "cocoa/custom_frame_view.h",
-        "cocoa/custom_frame_view.mm",
         "cocoa/dev_tools_controller.h",
         "cocoa/dev_tools_controller.mm",
         "cocoa/device_chooser_content_view_cocoa.h",
@@ -2855,8 +2853,6 @@
         "cocoa/floating_bar_backing_view.mm",
         "cocoa/framed_browser_window.h",
         "cocoa/framed_browser_window.mm",
-        "cocoa/full_size_content_window.h",
-        "cocoa/full_size_content_window.mm",
         "cocoa/fullscreen/fullscreen_menubar_tracker.h",
         "cocoa/fullscreen/fullscreen_menubar_tracker.mm",
         "cocoa/fullscreen/fullscreen_toolbar_animation_controller.h",
@@ -3085,6 +3081,8 @@
         "cocoa/tab_dialogs_views_mac.mm",
         "cocoa/tab_modal_confirm_dialog_mac.h",
         "cocoa/tab_modal_confirm_dialog_mac.mm",
+        "cocoa/tabbed_browser_window.h",
+        "cocoa/tabbed_browser_window.mm",
         "cocoa/tabs/alert_indicator_button_cocoa.h",
         "cocoa/tabs/alert_indicator_button_cocoa.mm",
         "cocoa/tabs/tab_controller.h",
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index bf3672f1..6b679473 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -76,7 +76,8 @@
 
   WebContents* web_contents = chrome::AddRestoredTab(
       browser_, navigations, tab_index, selected_navigation, extension_app_id,
-      select, pin, from_last_session, storage_namespace, user_agent_override);
+      select, pin, from_last_session, storage_namespace, user_agent_override,
+      false /* from_session_restore */);
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
   // The focused tab will be loaded by Browser, and TabLoader will load the
@@ -115,7 +116,8 @@
 
   WebContents* web_contents = chrome::ReplaceRestoredTab(
       browser_, navigations, selected_navigation, from_last_session,
-      extension_app_id, storage_namespace, user_agent_override);
+      extension_app_id, storage_namespace, user_agent_override,
+      false /* from_session_restore */);
 
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
diff --git a/chrome/browser/ui/browser_tabrestore.cc b/chrome/browser/ui/browser_tabrestore.cc
index 2b9c5b57..40c875d 100644
--- a/chrome/browser/ui/browser_tabrestore.cc
+++ b/chrome/browser/ui/browser_tabrestore.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/tab_contents/tab_util.h"
@@ -45,7 +46,8 @@
     bool from_last_session,
     content::SessionStorageNamespace* session_storage_namespace,
     const std::string& user_agent_override,
-    bool initially_hidden) {
+    bool initially_hidden,
+    bool from_session_restore) {
   GURL restore_url = navigations.at(selected_navigation).virtual_url();
   // TODO(ajwong): Remove the temporary session_storage_namespace_map when
   // we teach session restore to understand that one tab can have multiple
@@ -67,6 +69,8 @@
   WebContents* web_contents = content::WebContents::CreateWithSessionStorage(
       create_params,
       session_storage_namespace_map);
+  if (from_session_restore)
+    SessionRestore::OnWillRestoreTab(web_contents);
   extensions::TabHelper::CreateForWebContents(web_contents);
   extensions::TabHelper::FromWebContents(web_contents)->
       SetExtensionAppById(extension_app_id);
@@ -94,15 +98,12 @@
     bool pin,
     bool from_last_session,
     content::SessionStorageNamespace* session_storage_namespace,
-    const std::string& user_agent_override) {
-  WebContents* web_contents = CreateRestoredTab(browser,
-                                                navigations,
-                                                selected_navigation,
-                                                extension_app_id,
-                                                from_last_session,
-                                                session_storage_namespace,
-                                                user_agent_override,
-                                                !select);
+    const std::string& user_agent_override,
+    bool from_session_restore) {
+  WebContents* web_contents = CreateRestoredTab(
+      browser, navigations, selected_navigation, extension_app_id,
+      from_last_session, session_storage_namespace, user_agent_override,
+      !select, from_session_restore);
 
   int add_types = select ? TabStripModel::ADD_ACTIVE
                          : TabStripModel::ADD_NONE;
@@ -144,15 +145,12 @@
     bool from_last_session,
     const std::string& extension_app_id,
     content::SessionStorageNamespace* session_storage_namespace,
-    const std::string& user_agent_override) {
-  WebContents* web_contents = CreateRestoredTab(browser,
-                                                navigations,
-                                                selected_navigation,
-                                                extension_app_id,
-                                                from_last_session,
-                                                session_storage_namespace,
-                                                user_agent_override,
-                                                false);
+    const std::string& user_agent_override,
+    bool from_session_restore) {
+  WebContents* web_contents = CreateRestoredTab(
+      browser, navigations, selected_navigation, extension_app_id,
+      from_last_session, session_storage_namespace, user_agent_override, false,
+      from_session_restore);
 
   // ReplaceWebContentsAt won't animate in the restoration, so manually do the
   // equivalent of ReplaceWebContentsAt.
diff --git a/chrome/browser/ui/browser_tabrestore.h b/chrome/browser/ui/browser_tabrestore.h
index 79fa951..acf877a1 100644
--- a/chrome/browser/ui/browser_tabrestore.h
+++ b/chrome/browser/ui/browser_tabrestore.h
@@ -22,17 +22,18 @@
 
 namespace chrome {
 
-// Add a tab with its session history restored from the SessionRestore
-// system. If select is true, the tab is selected. |tab_index| gives the index
-// to insert the tab at. |selected_navigation| is the index of the
-// SerializedNavigationEntry in |navigations| to select. If |extension_app_id|
-// is non-empty the tab is an app tab and |extension_app_id| is the id of the
-// extension. If |pin| is true and |tab_index|/ is the last pinned tab, then
-// the newly created tab is pinned. If |from_last_session| is true,
-// |navigations| are from the previous session. |user_agent_override| contains
-// the string being used as the user agent for all of the tab's navigations when
-// the regular user agent is overridden. Returns the WebContents of the restored
-// tab.
+// Add a tab with its session history restored from the SessionRestore and
+// TabRestoreService systems. If select is true, the tab is selected.
+// |tab_index| gives the index to insert the tab at. |selected_navigation| is
+// the index of the SerializedNavigationEntry in |navigations| to select. If
+// |extension_app_id| is non-empty the tab is an app tab and |extension_app_id|
+// is the id of the extension. If |pin| is true and |tab_index|/ is the last
+// pinned tab, then the newly created tab is pinned. If |from_last_session| is
+// true, |navigations| are from the previous session. |user_agent_override|
+// contains the string being used as the user agent for all of the tab's
+// navigations when the regular user agent is overridden. If
+// |from_session_restore| is true, the restored tab is created by session
+// restore. Returns the WebContents of the restored tab.
 content::WebContents* AddRestoredTab(
     Browser* browser,
     const std::vector<sessions::SerializedNavigationEntry>& navigations,
@@ -43,11 +44,12 @@
     bool pin,
     bool from_last_session,
     content::SessionStorageNamespace* storage_namespace,
-    const std::string& user_agent_override);
+    const std::string& user_agent_override,
+    bool from_session_restore);
 
 // Replaces the state of the currently selected tab with the session
-// history restored from the SessionRestore system. Returns the WebContents of
-// the restored tab.
+// history restored from the SessionRestore and TabRestoreService systems.
+// Returns the WebContents of the restored tab.
 content::WebContents* ReplaceRestoredTab(
     Browser* browser,
     const std::vector<sessions::SerializedNavigationEntry>& navigations,
@@ -55,8 +57,8 @@
     bool from_last_session,
     const std::string& extension_app_id,
     content::SessionStorageNamespace* session_storage_namespace,
-    const std::string& user_agent_override);
-
+    const std::string& user_agent_override,
+    bool from_session_restore);
 
 }  // namespace chrome
 
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index df42604..3a6339a9 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -26,7 +26,6 @@
 #import "chrome/browser/ui/cocoa/browser_window_fullscreen_transition.h"
 #import "chrome/browser/ui/cocoa/browser_window_layout.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
-#import "chrome/browser/ui/cocoa/custom_frame_view.h"
 #import "chrome/browser/ui/cocoa/dev_tools_controller.h"
 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
@@ -61,6 +60,16 @@
 using content::RenderWidgetHostView;
 using content::WebContents;
 
+@interface NSView (PrivateAPI)
+// Returns the fullscreen button's origin in window coordinates. This method is
+// only available on NSThemeFrame (the contentView's superview), and it should
+// not be relied on to exist on macOS >10.9 (which doesn't have a separate
+// fullscreen button). TabbedBrowserWindow's NSThemeFrame subclass centers it
+// vertically in the tabstrip (if there is a tabstrip), and shifts it to the
+// left of the old-style avatar icon if necessary.
+- (NSPoint)_fullScreenButtonOrigin;
+@end
+
 namespace {
 
 // The screen on which the window was fullscreened, and whether the device had
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
index fdb74647..8f553ce 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
@@ -760,15 +760,7 @@
 TEST_F(BrowserWindowControllerTest, UsesAutoLayout) {
   // If Auto Layout is on, there will be synthesized constraints based on the
   // view's frame and autoresizing mask.
-  // TODO(sdy): Turn back on (or remove) after investigating a performance
-  // regression: https://crbug.com/706931
-  if (chrome::ShouldUseFullSizeContentView()) {
-    // FramedBrowserWindow relies on Auto Layout to position the window buttons
-    // when using a full size content view.
-    EXPECT_NE(0u, [[[controller_ chromeContentView] constraints] count]);
-  } else {
-    EXPECT_EQ(0u, [[[controller_ chromeContentView] constraints] count]);
-  }
+  EXPECT_EQ(0u, [[[controller_ chromeContentView] constraints] count]);
 }
 
 @interface BrowserWindowControllerFakeFullscreen : BrowserWindowController {
diff --git a/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
index 2855e87..8b09756b 100644
--- a/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
+++ b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
@@ -359,7 +359,7 @@
     NSRect relativeContentFinalFrame =
         NSMakeRect(contentViewOrigin.x, contentViewOrigin.y,
                    finalFrame_.size.width, finalFrame_.size.height);
-    [primaryWindow_ forceContentViewFrame:relativeContentFinalFrame];
+    [[primaryWindow_ contentView] setFrame:relativeContentFinalFrame];
 
     fullscreenTabStripBackgroundView_.reset(
         [[FullscreenTabStripBackgroundView alloc]
diff --git a/chrome/browser/ui/cocoa/chrome_browser_window.h b/chrome/browser/ui/cocoa/chrome_browser_window.h
index c4667b12..2974f34d 100644
--- a/chrome/browser/ui/cocoa/chrome_browser_window.h
+++ b/chrome/browser/ui/cocoa/chrome_browser_window.h
@@ -5,10 +5,10 @@
 #ifndef CHROME_BROWSER_UI_COCOA_CHROME_BROWSER_WINDOW_H_
 #define CHROME_BROWSER_UI_COCOA_CHROME_BROWSER_WINDOW_H_
 
-#import "chrome/browser/ui/cocoa/full_size_content_window.h"
+#import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
 
 // Common base class for chrome browser windows.
-@interface ChromeBrowserWindow : FullSizeContentWindow
+@interface ChromeBrowserWindow : ChromeEventProcessingWindow
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_CHROME_BROWSER_WINDOW_H_
diff --git a/chrome/browser/ui/cocoa/custom_frame_view.h b/chrome/browser/ui/cocoa/custom_frame_view.h
deleted file mode 100644
index 0f87124..0000000
--- a/chrome/browser/ui/cocoa/custom_frame_view.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_CUSTOM_FRAME_VIEW_H_
-#define CHROME_BROWSER_UI_COCOA_CUSTOM_FRAME_VIEW_H_
-
-#import <Cocoa/Cocoa.h>
-
-// CustomFrameView is a class whose methods we swizzle into NSGrayFrame
-// on 10.7 and below, or NSThemeFrame on 10.8 and above, so that we can
-// support custom frame drawing.
-// This class is never to be instantiated on its own.
-
-@interface NSView (CustomFrameView)
-
-// Returns where the fullscreen button's origin should be positioned in window
-// coordinates.
-// We swizzle NSThemeFrame's implementation to center it vertically in the
-// tabstrip (if there is a tabstrip), and to shift it to the left of the
-// old-style avatar icon if necessary.
-- (NSPoint)_fullScreenButtonOrigin;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_CUSTOM_FRAME_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/custom_frame_view.mm b/chrome/browser/ui/cocoa/custom_frame_view.mm
deleted file mode 100644
index e5bcefc..0000000
--- a/chrome/browser/ui/cocoa/custom_frame_view.mm
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/custom_frame_view.h"
-
-#import <Carbon/Carbon.h>
-#include <crt_externs.h>
-#import <objc/runtime.h>
-#include <string.h>
-
-#include "base/logging.h"
-#include "base/mac/mac_util.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
-
-@interface NSView (Swizzles)
-- (NSPoint)_fullScreenButtonOriginOriginal;
-@end
-
-@interface NSWindow (FramedBrowserWindow)
-- (NSPoint)fullScreenButtonOriginAdjustment;
-@end
-
-@interface CustomFrameView : NSView
-
-// Clang emits a warning if designated initializers don't call the super
-// initializer, even if the method raises an exception.
-// http://www.crbug.com/479019.
-- (id)initWithFrame:(NSRect)frame UNAVAILABLE_ATTRIBUTE;
-- (id)initWithCoder:(NSCoder*)coder UNAVAILABLE_ATTRIBUTE;
-
-@end
-
-@implementation CustomFrameView
-
-+ (void)load {
-  // Swizzling should only happen in the browser process. Interacting with
-  // AppKit will run +[borderViewClass initialize] in the renderer, which
-  // may establish Mach IPC with com.apple.windowserver.
-  // Note that CommandLine has not been initialized yet, since this is running
-  // as a module initializer.
-  const char* const* const argv = *_NSGetArgv();
-  const int argc = *_NSGetArgc();
-  const char kType[] = "--type=";
-  for (int i = 1; i < argc; ++i) {
-    const char* arg = argv[i];
-    if (strncmp(arg, kType, strlen(kType)) == 0)
-      return;
-  }
-
-  // In Yosemite, the fullscreen button replaces the zoom button. We no longer
-  // need to swizzle out this AppKit private method.
-  if (!base::mac::IsOS10_9())
-    return;
-
-  base::mac::ScopedNSAutoreleasePool pool;
-
-  // On 10.8+ the background for textured windows are no longer drawn by
-  // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
-  Class borderViewClass = NSClassFromString(@"NSThemeFrame");
-  DCHECK(borderViewClass);
-  if (!borderViewClass) return;
-
-  // Swizzle the method that sets the origin for the Lion fullscreen button.
-  // Do nothing if it cannot be found.
-  Method m0 = class_getInstanceMethod([self class],
-                               @selector(_fullScreenButtonOrigin));
-  if (m0) {
-    BOOL didAdd = class_addMethod(borderViewClass,
-                                  @selector(_fullScreenButtonOriginOriginal),
-                                  method_getImplementation(m0),
-                                  method_getTypeEncoding(m0));
-    if (didAdd) {
-      Method m1 = class_getInstanceMethod(borderViewClass,
-                                          @selector(_fullScreenButtonOrigin));
-      Method m2 = class_getInstanceMethod(
-          borderViewClass, @selector(_fullScreenButtonOriginOriginal));
-      if (m1 && m2) {
-        method_exchangeImplementations(m1, m2);
-      }
-    }
-  }
-}
-
-- (id)initWithFrame:(NSRect)frame {
-  // This class is not for instantiating.
-  [self doesNotRecognizeSelector:_cmd];
-  return nil;
-}
-
-- (id)initWithCoder:(NSCoder*)coder {
-  // This class is not for instantiating.
-  [self doesNotRecognizeSelector:_cmd];
-  return nil;
-}
-
-// Override to move the fullscreen button to the left of the profile avatar.
-- (NSPoint)_fullScreenButtonOrigin {
-  NSWindow* window = [self window];
-  NSPoint offset = NSZeroPoint;
-
-  if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)])
-    offset = [window fullScreenButtonOriginAdjustment];
-
-  NSPoint origin = [self _fullScreenButtonOriginOriginal];
-  origin.x += offset.x;
-  origin.y += offset.y;
-  return origin;
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/custom_frame_view_unittest.mm b/chrome/browser/ui/cocoa/custom_frame_view_unittest.mm
deleted file mode 100644
index 33339ad..0000000
--- a/chrome/browser/ui/cocoa/custom_frame_view_unittest.mm
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <Cocoa/Cocoa.h>
-#include <objc/runtime.h>
-
-#include "base/mac/mac_util.h"
-#include "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/custom_frame_view.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-class CustomFrameViewTest : public PlatformTest {
- public:
-  CustomFrameViewTest() {
-    NSRect frame = NSMakeRect(0, 0, 50, 50);
-    // We create NSGrayFrame instead of CustomFrameView because
-    // we are swizzling into NSThemeFrame.
-    Class customFrameClass = NSClassFromString(@"NSThemeFrame");
-    view_.reset([[customFrameClass alloc] initWithFrame:frame]);
-  }
-
-  base::scoped_nsobject<NSView> view_;
-};
-
-//  Test to make sure our class modifications were successful.
-TEST_F(CustomFrameViewTest, SuccessfulClassModifications) {
-  // In Yosemite, the fullscreen button replaces the zoom button. We no longer
-  // need to swizzle out this AppKit private method.
-  if (!base::mac::IsOS10_9())
-    return;
-
-  unsigned int count;
-  BOOL foundSwizzledMethod = NO;
-
-  Method* methods = class_copyMethodList([view_ class], &count);
-  for (unsigned int i = 0; i < count; ++i) {
-    SEL selector = method_getName(methods[i]);
-    if (selector == @selector(_fullScreenButtonOriginOriginal))
-      foundSwizzledMethod = YES;
-  }
-  EXPECT_TRUE(foundSwizzledMethod);
-  free(methods);
-}
diff --git a/chrome/browser/ui/cocoa/framed_browser_window.h b/chrome/browser/ui/cocoa/framed_browser_window.h
index 6acd269..2488946 100644
--- a/chrome/browser/ui/cocoa/framed_browser_window.h
+++ b/chrome/browser/ui/cocoa/framed_browser_window.h
@@ -9,16 +9,6 @@
 
 #include "chrome/browser/ui/cocoa/chrome_browser_window.h"
 
-// Offsets from the top/left of the window frame to the top of the window
-// controls (zoom, close, miniaturize) for a window with a tabstrip.
-const NSInteger kFramedWindowButtonsWithTabStripOffsetFromTop = 11;
-const NSInteger kFramedWindowButtonsWithTabStripOffsetFromLeft = 11;
-
-// Offsets from the top/left of the window frame to the top of the window
-// controls (zoom, close, miniaturize) for a window without a tabstrip.
-const NSInteger kFramedWindowButtonsWithoutTabStripOffsetFromTop = 3;
-const NSInteger kFramedWindowButtonsWithoutTabStripOffsetFromLeft = 7;
-
 // Cocoa class representing a framed browser window.
 // We need to override NSWindow with our own class since we need access to all
 // unhandled keyboard events and subclassing NSWindow is the only method to do
@@ -26,25 +16,26 @@
 @interface FramedBrowserWindow : ChromeBrowserWindow {
  @private
   BOOL shouldHideTitle_;
-  BOOL hasTabStrip_;
-  NSButton* closeButton_;
-  NSButton* miniaturizeButton_;
-  NSButton* zoomButton_;
 
   // Locks the window's frame and style mask. If it's set to YES, then the
   // frame and the style mask cannot be changed.
   BOOL styleMaskLock_;
-
-  CGFloat windowButtonsInterButtonSpacing_;
 }
 
 // The amount of window background image that is painted at the top of the
 // window, so that it shows behind the tap strip area.
 + (CGFloat)browserFrameViewPaintHeight;
 
-// Designated initializer.
-- (id)initWithContentRect:(NSRect)contentRect
-              hasTabStrip:(BOOL)hasTabStrip;
+// The style mask which -initWithContentRect: will use to create the window.
+// May be overridden by subclasses.
++ (NSUInteger)defaultStyleMask;
+
+- (id)initWithContentRect:(NSRect)contentRect NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithContentRect:(NSRect)contentRect
+                          styleMask:(NSWindowStyleMask)style
+                            backing:(NSBackingStoreType)bufferingType
+                              defer:(BOOL)flag NS_UNAVAILABLE;
 
 // Tells the window to suppress title drawing.
 - (void)setShouldHideTitle:(BOOL)flag;
@@ -59,13 +50,6 @@
 // when frameAndStyleMaskLock_ is set to true.
 - (void)setStyleMask:(NSUInteger)styleMask;
 
-// Returns the desired spacing between window control views.
-- (CGFloat)windowButtonsInterButtonSpacing;
-
-// Called by CustomFrameView to determine a custom location for the Lion
-// fullscreen button. Returns NSZeroPoint to use the Lion default.
-- (NSPoint)fullScreenButtonOriginAdjustment;
-
 // Draws the window theme into the specified rect. Returns whether a theme was
 // drawn (whether incognito or full pattern theme; an overlay image doesn't
 // count).
diff --git a/chrome/browser/ui/cocoa/framed_browser_window.mm b/chrome/browser/ui/cocoa/framed_browser_window.mm
index acd5343..6a1cff36 100644
--- a/chrome/browser/ui/cocoa/framed_browser_window.mm
+++ b/chrome/browser/ui/cocoa/framed_browser_window.mm
@@ -20,7 +20,6 @@
 #import "chrome/browser/ui/cocoa/browser_window_touch_bar.h"
 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
 #include "chrome/browser/ui/cocoa/l10n_util.h"
-#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
 #include "chrome/grit/theme_resources.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
@@ -28,63 +27,8 @@
 #import "ui/base/cocoa/nsview_additions.h"
 #import "ui/base/cocoa/touch_bar_forward_declarations.h"
 
-// Implementer's note: Moving the window controls is tricky. When altering the
-// code, ensure that:
-// - accessibility hit testing works
-// - the accessibility hierarchy is correct
-// - close/min in the background don't bring the window forward
-// - rollover effects work correctly
-
-// The NSLayoutConstraint class hierarchy only exists in the 10.11 SDK. When
-// targeting something lower, constraintEqualToAnchor:constant: needs to be
-// invoked using duck typing.
-#if !defined(MAC_OS_X_VERSION_10_11) || \
-    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
-@interface NSObject (NSLayoutConstraint)
-- (NSLayoutConstraint*)constraintEqualToAnchor:(id)anchor constant:(CGFloat)c;
-- (NSLayoutConstraint*)constraintEqualToAnchor:(id)anchor;
-@end
-#endif
-
-namespace {
-
-// Size of the gradient. Empirically determined so that the gradient looks
-// like what the heuristic does when there are just a few tabs.
-const CGFloat kWindowGradientHeight = 24.0;
-
-}
-
-@interface FramedBrowserWindow (Private)
-
-// Updates the title bar's frame so it moves the windows buttons to correct
-// location (frame bottom is moved down so the buttons are moved down as well).
-- (void)adjustTitlebarContainer:(NSView*)titlebarContainer;
-// Adds layout constraints to window buttons, respecting flag returned by
-// |ShouldFlipWindowControlsInRTL| method.
-- (void)setWindowButtonsConstraints;
-// Replaces -[NSThemeFrame addTrackingArea:] with implementation that ignores
-// tracking rect if its size is the same as the size of window buttons rect
-// (rect where close, miniaturize and zoom buttons are located). This is
-// needed to workaround macOS bug (rdar://28535344) which unnecessarily adds
-// window buttons tracking rect even if those buttons were moved.
-// TODO(crbug.com/651287): Remove this workaround once macOS bug is fixed.
-- (void)forbidAddingWindowButtonsTrackingArea;
-// Called when titlebar container changes its frame. This method adjusts
-// titlebar container with correct frame.
-- (void)titlebarDidChangeFrameNotification:(NSNotification*)notification;
-// Adds layout constraints to the given window button so it displayed at correct
-// location. This respects flag returned by |ShouldFlipWindowControlsInRTL|
-// method.
-- (void)setLeadingOffset:(CGFloat)leadingOffset
-                toButton:(NSWindowButton)buttonType;
-
-- (void)adjustCloseButton:(NSNotification*)notification;
-- (void)adjustMiniaturizeButton:(NSNotification*)notification;
-- (void)adjustZoomButton:(NSNotification*)notification;
-- (void)adjustButton:(NSButton*)button
-              ofKind:(NSWindowButton)kind;
+@interface FramedBrowserWindow ()
 - (void)childWindowsDidChange;
-
 @end
 
 @implementation FramedBrowserWindow
@@ -94,99 +38,26 @@
                                                 : 60.0;
 }
 
++ (NSUInteger)defaultStyleMask {
+  return NSTitledWindowMask | NSClosableWindowMask |
+         NSMiniaturizableWindowMask | NSResizableWindowMask |
+         NSTexturedBackgroundWindowMask;
+}
+
 - (void)setStyleMask:(NSUInteger)styleMask {
   if (styleMaskLock_)
     return;
   [super setStyleMask:styleMask];
 }
 
-- (id)initWithContentRect:(NSRect)contentRect
-              hasTabStrip:(BOOL)hasTabStrip{
-  NSUInteger styleMask = NSTitledWindowMask |
-                         NSClosableWindowMask |
-                         NSMiniaturizableWindowMask |
-                         NSResizableWindowMask |
-                         NSTexturedBackgroundWindowMask;
-  bool shouldUseFullSizeContentView =
-      chrome::ShouldUseFullSizeContentView() && hasTabStrip;
-  if (shouldUseFullSizeContentView) {
-    if (@available(macOS 10.10, *)) {
-      styleMask |= NSFullSizeContentViewWindowMask;
-    }
-  }
-
+- (id)initWithContentRect:(NSRect)contentRect {
   if ((self = [super initWithContentRect:contentRect
-                               styleMask:styleMask
+                               styleMask:[[self class] defaultStyleMask]
                                  backing:NSBackingStoreBuffered
-                                   defer:YES
-                  wantsViewsOverTitlebar:hasTabStrip])) {
+                                   defer:YES])) {
     // The 10.6 fullscreen code copies the title to a different window, which
     // will assert if it's nil.
     [self setTitle:@""];
-
-    // The following two calls fix http://crbug.com/25684 by preventing the
-    // window from recalculating the border thickness as the window is
-    // resized.
-    // This was causing the window tint to change for the default system theme
-    // when the window was being resized.
-    [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
-    [self setContentBorderThickness:kWindowGradientHeight forEdge:NSMaxYEdge];
-
-    hasTabStrip_ = hasTabStrip;
-    closeButton_ = [self standardWindowButton:NSWindowCloseButton];
-    miniaturizeButton_ = [self standardWindowButton:NSWindowMiniaturizeButton];
-    zoomButton_ = [self standardWindowButton:NSWindowZoomButton];
-
-    windowButtonsInterButtonSpacing_ =
-        NSMinX([miniaturizeButton_ frame]) - NSMaxX([closeButton_ frame]);
-    if (windowButtonsInterButtonSpacing_ < 0)
-      // Sierra RTL
-      windowButtonsInterButtonSpacing_ =
-          NSMinX([miniaturizeButton_ frame]) - NSMaxX([zoomButton_ frame]);
-
-    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
-    if (shouldUseFullSizeContentView) {
-      // If Chrome uses full sized content view then window buttons are placed
-      // inside titlebar (which height is 22 points). In order to move window
-      // buttons down the whole toolbar should be moved down.
-      DCHECK(closeButton_);
-      NSView* titlebarContainer = [[closeButton_ superview] superview];
-      [self adjustTitlebarContainer:titlebarContainer];
-      [center addObserver:self
-                 selector:@selector(titlebarDidChangeFrameNotification:)
-                     name:NSViewFrameDidChangeNotification
-                   object:titlebarContainer];
-      // Window buttons are not movable unless their positioning is forced via
-      // layout constraints.
-      [self setWindowButtonsConstraints];
-      // Remove an extra tracking rect unnecessarily added by AppKit which
-      // highlights the buttons on mouse enter event. That rect is added where
-      // buttons used to be previously.
-      [self forbidAddingWindowButtonsTrackingArea];
-    } else if (hasTabStrip_) {
-      // If Chrome does not use a full sized content view then AppKit adds the
-      // window buttons to the root view, where they must be manually
-      // re-positioned.
-      [self adjustButton:closeButton_ ofKind:NSWindowCloseButton];
-      [self adjustButton:miniaturizeButton_ ofKind:NSWindowMiniaturizeButton];
-      [self adjustButton:zoomButton_ ofKind:NSWindowZoomButton];
-      [closeButton_ setPostsFrameChangedNotifications:YES];
-      [miniaturizeButton_ setPostsFrameChangedNotifications:YES];
-      [zoomButton_ setPostsFrameChangedNotifications:YES];
-
-      [center addObserver:self
-                 selector:@selector(adjustCloseButton:)
-                     name:NSViewFrameDidChangeNotification
-                   object:closeButton_];
-      [center addObserver:self
-                 selector:@selector(adjustMiniaturizeButton:)
-                     name:NSViewFrameDidChangeNotification
-                   object:miniaturizeButton_];
-      [center addObserver:self
-                 selector:@selector(adjustZoomButton:)
-                     name:NSViewFrameDidChangeNotification
-                   object:zoomButton_];
-    }
   }
 
   return self;
@@ -204,187 +75,6 @@
   return [super windowTitlebarLayoutDirection];
 }
 
-- (void)adjustTitlebarContainer:(NSView*)titlebarContainer {
-  if ([self styleMask] & NSFullScreenWindowMask)
-    return;
-
-  DCHECK(chrome::ShouldUseFullSizeContentView());
-  DCHECK([NSStringFromClass([titlebarContainer class])
-      isEqual:@"NSTitlebarContainerView"]);
-
-  NSRect newFrame = [titlebarContainer frame];
-  NSRect superviewFrame = [[titlebarContainer superview] frame];
-  // Increase toolbar height to move window buttons down where they should be.
-  newFrame.size.height =
-      floor((chrome::kTabStripHeight + NSHeight([closeButton_ frame])) / 2.0);
-  newFrame.size.width = NSWidth(superviewFrame);
-  newFrame.origin.y = NSHeight(superviewFrame) - NSHeight(newFrame);
-  newFrame.origin.x = NSMinX(superviewFrame);
-  [titlebarContainer setFrame:newFrame];
-}
-
-- (void)setWindowButtonsConstraints {
-  DCHECK(chrome::ShouldUseFullSizeContentView());
-
-  CGFloat leadingOffset =
-      hasTabStrip_ ? kFramedWindowButtonsWithTabStripOffsetFromLeft
-                   : kFramedWindowButtonsWithoutTabStripOffsetFromLeft;
-  [self setLeadingOffset:leadingOffset toButton:NSWindowCloseButton];
-
-  leadingOffset +=
-      windowButtonsInterButtonSpacing_ + NSWidth([closeButton_ frame]);
-  [self setLeadingOffset:leadingOffset toButton:NSWindowMiniaturizeButton];
-
-  leadingOffset +=
-      windowButtonsInterButtonSpacing_ + NSWidth([miniaturizeButton_ frame]);
-  [self setLeadingOffset:leadingOffset toButton:NSWindowZoomButton];
-}
-
-- (void)forbidAddingWindowButtonsTrackingArea {
-  DCHECK(chrome::ShouldUseFullSizeContentView());
-
-  static dispatch_once_t onceToken;
-  dispatch_once(&onceToken, ^{
-    NSView* themeFrame = [[[closeButton_ superview] superview] superview];
-    Class themeFrameClass = [themeFrame class];
-    DCHECK([NSStringFromClass(themeFrameClass) isEqual:@"NSThemeFrame"]);
-    SEL addTrackingAreaSelector = @selector(addTrackingArea:);
-    Method originalMethod =
-        class_getInstanceMethod(themeFrameClass, addTrackingAreaSelector);
-    IMP originalImp = method_getImplementation(originalMethod);
-    NSRect windowButtonsRect = NSUnionRect(
-        NSUnionRect([closeButton_ frame], [miniaturizeButton_ frame]),
-        [zoomButton_ frame]);
-    NSSize buttonsAreaSize = NSIntegralRect(windowButtonsRect).size;
-
-    // |newImp| is never released with |imp_removeBlock|.
-    IMP newImp = imp_implementationWithBlock(^(id self, id area) {
-      // There is no other way to ensure that |area| is responsible for buttons
-      // highlighting except by relying on its size.
-      if (!NSEqualSizes(buttonsAreaSize, NSIntegralRect([area rect]).size)) {
-        originalImp(self, addTrackingAreaSelector, area);
-      }
-    });
-
-    // Do not use base::mac::ScopedObjCClassSwizzler as it replaces existing
-    // implementation which is defined in NSView and will affect the whole app
-    // performance.
-    class_replaceMethod(themeFrameClass, addTrackingAreaSelector, newImp,
-                        method_getTypeEncoding(originalMethod));
-  });
-}
-
-- (void)titlebarDidChangeFrameNotification:(NSNotification*)notification {
-  [self adjustTitlebarContainer:[notification object]];
-}
-
-- (void)setLeadingOffset:(CGFloat)leadingOffset
-                toButton:(NSWindowButton)buttonType {
-  DCHECK(chrome::ShouldUseFullSizeContentView());
-
-  NSButton* button = [self standardWindowButton:buttonType];
-  [button setTranslatesAutoresizingMaskIntoConstraints:NO];
-
-  if (@available(macOS 10.11, *)) {
-    // Do not use leadingAnchor because |ShouldFlipWindowControlsInRTL|
-    // should determine if current locale is RTL.
-    NSLayoutXAxisAnchor* leadingSourceAnchor = [button leftAnchor];
-    NSLayoutXAxisAnchor* leadingTargetAnchor = [[button superview] leftAnchor];
-    if (cocoa_l10n_util::ShouldFlipWindowControlsInRTL()) {
-      leadingSourceAnchor = [button rightAnchor];
-      leadingTargetAnchor = [[button superview] rightAnchor];
-      leadingOffset = -leadingOffset;
-    }
-
-#if !defined(MAC_OS_X_VERSION_10_11) || \
-    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
-    id leadingSourceAnchorDuck = leadingSourceAnchor;
-#else
-    NSLayoutXAxisAnchor* leadingSourceAnchorDuck = leadingSourceAnchor;
-#endif
-    [[leadingSourceAnchorDuck constraintEqualToAnchor:leadingTargetAnchor
-                                             constant:leadingOffset]
-        setActive:YES];
-
-    [[[button bottomAnchor]
-        constraintEqualToAnchor:[[button superview] bottomAnchor]]
-        setActive:YES];
-  } else {
-    NOTREACHED();
-  }
-}
-
-- (void)adjustCloseButton:(NSNotification*)notification {
-  [self adjustButton:[notification object]
-              ofKind:NSWindowCloseButton];
-}
-
-- (void)adjustMiniaturizeButton:(NSNotification*)notification {
-  [self adjustButton:[notification object]
-              ofKind:NSWindowMiniaturizeButton];
-}
-
-- (void)adjustZoomButton:(NSNotification*)notification {
-  [self adjustButton:[notification object]
-              ofKind:NSWindowZoomButton];
-}
-
-- (void)adjustButton:(NSButton*)button
-              ofKind:(NSWindowButton)kind {
-  NSRect buttonFrame = [button frame];
-
-  CGFloat xOffset = hasTabStrip_
-      ? kFramedWindowButtonsWithTabStripOffsetFromLeft
-      : kFramedWindowButtonsWithoutTabStripOffsetFromLeft;
-  CGFloat yOffset = hasTabStrip_
-      ? kFramedWindowButtonsWithTabStripOffsetFromTop
-      : kFramedWindowButtonsWithoutTabStripOffsetFromTop;
-  buttonFrame.origin =
-      NSMakePoint(xOffset, (NSHeight([self frame]) -
-                            NSHeight(buttonFrame) - yOffset));
-
-  switch (kind) {
-    case NSWindowZoomButton:
-      buttonFrame.origin.x += NSWidth([miniaturizeButton_ frame]);
-      buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
-      // fallthrough
-    case NSWindowMiniaturizeButton:
-      buttonFrame.origin.x += NSWidth([closeButton_ frame]);
-      buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
-      // fallthrough
-    default:
-      break;
-  }
-
-  if (cocoa_l10n_util::ShouldFlipWindowControlsInRTL()) {
-    buttonFrame.origin.x =
-        NSWidth([self frame]) - buttonFrame.origin.x - NSWidth([button frame]);
-  }
-
-  BOOL didPost = [button postsBoundsChangedNotifications];
-  [button setPostsFrameChangedNotifications:NO];
-  [button setFrame:buttonFrame];
-  [button setPostsFrameChangedNotifications:didPost];
-}
-
-// The tab strip view covers our window buttons. So we add hit testing here
-// to find them properly and return them to the accessibility system.
-- (id)accessibilityHitTest:(NSPoint)point {
-  NSPoint windowPoint = ui::ConvertPointFromScreenToWindow(self, point);
-  NSControl* controls[] = { closeButton_, zoomButton_, miniaturizeButton_ };
-  id value = nil;
-  for (size_t i = 0; i < sizeof(controls) / sizeof(controls[0]); ++i) {
-    if (NSPointInRect(windowPoint, [controls[i] frame])) {
-      value = [controls[i] accessibilityHitTest:point];
-      break;
-    }
-  }
-  if (!value) {
-    value = [super accessibilityHitTest:point];
-  }
-  return value;
-}
-
 - (void)setShouldHideTitle:(BOOL)flag {
   shouldHideTitle_ = flag;
 }
@@ -397,10 +87,6 @@
   return shouldHideTitle_;
 }
 
-- (CGFloat)windowButtonsInterButtonSpacing {
-  return windowButtonsInterButtonSpacing_;
-}
-
 // This method is called whenever a window is moved in order to ensure it fits
 // on the screen.  We cannot always handle resizes without breaking, so we
 // prevent frame constraining in those cases.
@@ -415,29 +101,6 @@
   return [super constrainFrameRect:frame toScreen:screen];
 }
 
-- (NSPoint)fullScreenButtonOriginAdjustment {
-  if (!hasTabStrip_)
-    return NSZeroPoint;
-
-  // Vertically center the button.
-  NSPoint origin = NSMakePoint(0, -6);
-
-  // If there is a profile avatar icon present, shift the button over by its
-  // width and some padding. The new avatar button is displayed to the right
-  // of the fullscreen icon, so it doesn't need to be shifted.
-  BrowserWindowController* bwc =
-      base::mac::ObjCCastStrict<BrowserWindowController>(
-          [self windowController]);
-  if ([bwc shouldShowAvatar] && ![bwc shouldUseNewAvatarButton]) {
-    NSView* avatarButton = [[bwc avatarButtonController] view];
-    origin.x = -(NSWidth([avatarButton frame]) + 3);
-  } else {
-    origin.x -= 6;
-  }
-
-  return origin;
-}
-
 + (BOOL)drawWindowThemeInDirtyRect:(NSRect)dirtyRect
                            forView:(NSView*)view
                             bounds:(NSRect)bounds
diff --git a/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm b/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm
index 0ee3491..30da899 100644
--- a/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm
+++ b/chrome/browser/ui/cocoa/framed_browser_window_unittest.mm
@@ -15,13 +15,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-
-namespace {
-NSString* const kAppleTextDirectionDefaultsKey = @"AppleTextDirection";
-NSString* const kForceRTLWritingDirectionDefaultsKey =
-    @"NSForceRightToLeftWritingDirection";
-}  // namespace
 
 class FramedBrowserWindowTest : public CocoaTest {
  public:
@@ -29,8 +22,7 @@
     CocoaTest::SetUp();
     // Create a window.
     window_ = [[FramedBrowserWindow alloc]
-               initWithContentRect:NSMakeRect(0, 0, 800, 600)
-                       hasTabStrip:YES];
+        initWithContentRect:NSMakeRect(0, 0, 800, 600)];
     if (base::debug::BeingDebugged()) {
       [window_ orderFront:nil];
     } else {
@@ -94,206 +86,3 @@
   EXPECT_TRUE([window_ _isTitleHidden]);
   EXPECT_TRUE([emptyTitleData isEqualToData:hiddenTitleData]);
 }
-
-// Test to make sure that our window widgets are in the right place.
-TEST_F(FramedBrowserWindowTest, WindowWidgetLocation) {
-  BOOL yes = YES;
-  BOOL no = NO;
-
-  // First without a tabstrip.
-  [window_ close];
-  window_ = [[FramedBrowserWindow alloc]
-             initWithContentRect:NSMakeRect(0, 0, 800, 600)
-                     hasTabStrip:NO];
-  // Update window layout according to existing layout constraints.
-  [window_ layoutIfNeeded];
-  id controller = [OCMockObject mockForClass:[BrowserWindowController class]];
-  [[[controller stub] andReturnValue:OCMOCK_VALUE(yes)]
-      isKindOfClass:[BrowserWindowController class]];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] hasTabStrip];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] hasTitleBar];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] isTabbedWindow];
-  [window_ setWindowController:controller];
-
-  NSView* closeBoxControl = [window_ standardWindowButton:NSWindowCloseButton];
-  EXPECT_TRUE(closeBoxControl);
-  NSRect closeBoxFrame = [closeBoxControl convertRect:[closeBoxControl bounds]
-                                               toView:nil];
-  NSRect windowBounds = [window_ frame];
-  windowBounds = [[window_ contentView] convertRect:windowBounds fromView:nil];
-  windowBounds.origin = NSZeroPoint;
-  EXPECT_EQ(NSMaxY(closeBoxFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithoutTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(closeBoxFrame),
-            kFramedWindowButtonsWithoutTabStripOffsetFromLeft);
-
-  NSView* miniaturizeControl =
-      [window_ standardWindowButton:NSWindowMiniaturizeButton];
-  EXPECT_TRUE(miniaturizeControl);
-  NSRect miniaturizeFrame =
-      [miniaturizeControl convertRect:[miniaturizeControl bounds]
-                               toView:nil];
-  EXPECT_LT(NSMaxX(closeBoxFrame), NSMinX(miniaturizeFrame));
-  EXPECT_EQ(NSMaxY(miniaturizeFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithoutTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(miniaturizeFrame),
-            NSMaxX(closeBoxFrame) + [window_ windowButtonsInterButtonSpacing]);
-  [window_ setWindowController:nil];
-
-  // Then with a tabstrip.
-  [window_ close];
-  window_ = [[FramedBrowserWindow alloc]
-             initWithContentRect:NSMakeRect(0, 0, 800, 600)
-                     hasTabStrip:YES];
-  // Update window layout according to existing layout constraints.
-  [window_ layoutIfNeeded];
-  controller = [OCMockObject mockForClass:[BrowserWindowController class]];
-  [[[controller stub] andReturnValue:OCMOCK_VALUE(yes)]
-      isKindOfClass:[BrowserWindowController class]];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] hasTabStrip];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] hasTitleBar];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] isTabbedWindow];
-  [window_ setWindowController:controller];
-
-  closeBoxControl = [window_ standardWindowButton:NSWindowCloseButton];
-  EXPECT_TRUE(closeBoxControl);
-  closeBoxFrame = [closeBoxControl convertRect:[closeBoxControl bounds]
-                                        toView:nil];
-  windowBounds = [window_ frame];
-  windowBounds = [[window_ contentView] convertRect:windowBounds fromView:nil];
-  windowBounds.origin = NSZeroPoint;
-  EXPECT_EQ(NSMaxY(closeBoxFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(closeBoxFrame),
-            kFramedWindowButtonsWithTabStripOffsetFromLeft);
-
-  miniaturizeControl = [window_ standardWindowButton:NSWindowMiniaturizeButton];
-  EXPECT_TRUE(miniaturizeControl);
-  miniaturizeFrame = [miniaturizeControl convertRect:[miniaturizeControl bounds]
-                                              toView:nil];
-  EXPECT_EQ(NSMaxY(miniaturizeFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(miniaturizeFrame),
-            NSMaxX(closeBoxFrame) + [window_ windowButtonsInterButtonSpacing]);
-  [window_ setWindowController:nil];
-}
-
-class FramedBrowserWindowRTLTest : public FramedBrowserWindowTest {
-  void SetUp() override {
-    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-    originalAppleTextDirection_ =
-        [defaults boolForKey:kAppleTextDirectionDefaultsKey];
-    originalRTLWritingDirection_ =
-        [defaults boolForKey:kForceRTLWritingDirectionDefaultsKey];
-    [defaults setBool:YES forKey:kAppleTextDirectionDefaultsKey];
-    [defaults setBool:YES forKey:kForceRTLWritingDirectionDefaultsKey];
-    FramedBrowserWindowTest::SetUp();
-  }
-
-  void TearDown() override {
-    FramedBrowserWindowTest::TearDown();
-    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-    [defaults setBool:originalAppleTextDirection_
-               forKey:kAppleTextDirectionDefaultsKey];
-    [defaults setBool:originalRTLWritingDirection_
-               forKey:kForceRTLWritingDirectionDefaultsKey];
-  }
-
- private:
-  BOOL originalAppleTextDirection_;
-  BOOL originalRTLWritingDirection_;
-};
-
-// Test to make sure that our window widgets are in the right place.
-// Currently, this is the same exact test as above, since RTL button
-// layout is behind the ExperimentalMacRTL flag. However, this ensures
-// that our calculations are correct for Sierra RTL, which lays out
-// the window buttons in reverse by default. See crbug/662079.
-TEST_F(FramedBrowserWindowRTLTest, WindowWidgetLocation) {
-  BOOL yes = YES;
-  BOOL no = NO;
-
-  // First without a tabstrip.
-  [window_ close];
-  window_ = [[FramedBrowserWindow alloc]
-      initWithContentRect:NSMakeRect(0, 0, 800, 600)
-              hasTabStrip:NO];
-  // Update window layout according to existing layout constraints.
-  [window_ layoutIfNeeded];
-  id controller = [OCMockObject mockForClass:[BrowserWindowController class]];
-  [[[controller stub] andReturnValue:OCMOCK_VALUE(yes)]
-      isKindOfClass:[BrowserWindowController class]];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] hasTabStrip];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] hasTitleBar];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] isTabbedWindow];
-  [window_ setWindowController:controller];
-
-  NSView* closeBoxControl = [window_ standardWindowButton:NSWindowCloseButton];
-  EXPECT_TRUE(closeBoxControl);
-  NSRect closeBoxFrame =
-      [closeBoxControl convertRect:[closeBoxControl bounds] toView:nil];
-  NSRect windowBounds = [window_ frame];
-  windowBounds = [[window_ contentView] convertRect:windowBounds fromView:nil];
-  windowBounds.origin = NSZeroPoint;
-  EXPECT_EQ(
-      NSMaxY(closeBoxFrame),
-      NSMaxY(windowBounds) - kFramedWindowButtonsWithoutTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(closeBoxFrame),
-            kFramedWindowButtonsWithoutTabStripOffsetFromLeft);
-
-  NSView* miniaturizeControl =
-      [window_ standardWindowButton:NSWindowMiniaturizeButton];
-  EXPECT_TRUE(miniaturizeControl);
-  NSRect miniaturizeFrame =
-      [miniaturizeControl convertRect:[miniaturizeControl bounds] toView:nil];
-  EXPECT_LT(NSMaxX(closeBoxFrame), NSMinX(miniaturizeFrame));
-  EXPECT_EQ(NSMaxY(miniaturizeFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithoutTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(miniaturizeFrame),
-            NSMaxX(closeBoxFrame) + [window_ windowButtonsInterButtonSpacing]);
-  [window_ setWindowController:nil];
-
-  // Then with a tabstrip.
-  [window_ close];
-  window_ = [[FramedBrowserWindow alloc]
-             initWithContentRect:NSMakeRect(0, 0, 800, 600)
-                     hasTabStrip:YES];
-  // Update window layout according to existing layout constraints.
-  [window_ layoutIfNeeded];
-  controller = [OCMockObject mockForClass:[BrowserWindowController class]];
-  [[[controller stub] andReturnValue:OCMOCK_VALUE(yes)]
-      isKindOfClass:[BrowserWindowController class]];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] hasTabStrip];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(no)] hasTitleBar];
-  [[[controller expect] andReturnValue:OCMOCK_VALUE(yes)] isTabbedWindow];
-  [window_ setWindowController:controller];
-
-  closeBoxControl = [window_ standardWindowButton:NSWindowCloseButton];
-  EXPECT_TRUE(closeBoxControl);
-  closeBoxFrame = [closeBoxControl convertRect:[closeBoxControl bounds]
-                                        toView:nil];
-  windowBounds = [window_ frame];
-  windowBounds = [[window_ contentView] convertRect:windowBounds fromView:nil];
-  windowBounds.origin = NSZeroPoint;
-  EXPECT_EQ(NSMaxY(closeBoxFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(closeBoxFrame),
-            kFramedWindowButtonsWithTabStripOffsetFromLeft);
-
-  miniaturizeControl = [window_ standardWindowButton:NSWindowMiniaturizeButton];
-  EXPECT_TRUE(miniaturizeControl);
-  miniaturizeFrame = [miniaturizeControl convertRect:[miniaturizeControl bounds]
-                                              toView:nil];
-  EXPECT_EQ(NSMaxY(miniaturizeFrame),
-            NSMaxY(windowBounds) -
-                kFramedWindowButtonsWithTabStripOffsetFromTop);
-  EXPECT_EQ(NSMinX(miniaturizeFrame),
-            NSMaxX(closeBoxFrame) + [window_ windowButtonsInterButtonSpacing]);
-  [window_ setWindowController:nil];
-}
diff --git a/chrome/browser/ui/cocoa/full_size_content_window.h b/chrome/browser/ui/cocoa/full_size_content_window.h
deleted file mode 100644
index 078d9fb..0000000
--- a/chrome/browser/ui/cocoa/full_size_content_window.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_FULL_SIZE_CONTENT_WINDOW_H_
-#define CHROME_BROWSER_UI_COCOA_FULL_SIZE_CONTENT_WINDOW_H_
-
-#import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
-
-#include "base/mac/scoped_nsobject.h"
-
-// By default, the contentView does not occupy the full size of a framed
-// window. Chrome still wants to draw in the title bar. Historically, Chrome
-// has done this by adding subviews directly to the root view. This causes
-// several problems. The most egregious is related to layer ordering when the
-// root view does not have a layer. By giving the contentView the same size as
-// the window, there is no longer any need to add subviews to the root view.
-//
-// If the window does not have a titlebar, then its contentView already has the
-// same size as the window. In this case, this class has no effect.
-//
-// This class currently does not support changing the window's style after the
-// window has been initialized.
-@interface FullSizeContentWindow : ChromeEventProcessingWindow {
- @private
-  // Holds the view that replaces [window contentView]. This view has the same
-  // size as the window.  Empty if there is no titlebar.
-  base::scoped_nsobject<NSView> chromeWindowView_;
-}
-
-// Designated initializer.
-- (instancetype)initWithContentRect:(NSRect)contentRect
-                          styleMask:(NSUInteger)windowStyle
-                            backing:(NSBackingStoreType)bufferingType
-                              defer:(BOOL)deferCreation
-             wantsViewsOverTitlebar:(BOOL)wantsViewsOverTitlebar;
-
-// Forces |chromeWindowView_| to resize to the given size. This need to be
-// forced because by default, the contentView will always have the same size
-// as the window. If |chromeWindowView_| is empty, we will set the frame of
-// [window contentView] to the given frame.
-- (void)forceContentViewFrame:(NSRect)frame;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_FULL_SIZE_CONTENT_WINDOW_H_
diff --git a/chrome/browser/ui/cocoa/full_size_content_window.mm b/chrome/browser/ui/cocoa/full_size_content_window.mm
deleted file mode 100644
index 3b4c82b2..0000000
--- a/chrome/browser/ui/cocoa/full_size_content_window.mm
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/full_size_content_window.h"
-
-#include <crt_externs.h>
-
-#include "base/auto_reset.h"
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_objc_class_swizzler.h"
-#import "chrome/browser/ui/cocoa/browser_window_layout.h"
-
-@interface FullSizeContentWindow ()
-
-+ (BOOL)shouldUseFullSizeContentViewForStyle:(NSUInteger)windowStyle;
-
-@end
-
-// This view always takes the size of its superview. It is intended to be used
-// as a NSWindow's contentView.  It is needed because NSWindow's implementation
-// explicitly resizes the contentView at inopportune times.
-@interface FullSizeContentView : NSView {
-  BOOL forceFrameFlag_;
-}
-
-// This method allows us to set the content view size since setFrameSize is
-// overridden to prevent the view from shrinking.
-- (void)forceFrame:(NSRect)frame;
-
-@end
-
-@implementation FullSizeContentView
-
-// This method is directly called by AppKit during a live window resize.
-// Override it to prevent the content view from shrinking.
-- (void)setFrameSize:(NSSize)size {
-  if ([self superview] && !forceFrameFlag_)
-    size = [[self superview] bounds].size;
-  [super setFrameSize:size];
-}
-
-- (void)forceFrame:(NSRect)frame {
-  forceFrameFlag_ = YES;
-  [super setFrame:frame];
-  forceFrameFlag_ = NO;
-}
-
-@end
-
-static bool g_disable_callstacksymbols = false;
-static IMP g_original_callstacksymbols_implementation;
-
-@interface FullSizeContentWindowSwizzlingSupport : NSObject
-@end
-
-@implementation FullSizeContentWindowSwizzlingSupport
-
-// This method replaces [NSThread callStackSymbols] via swizzling - see +load
-// below.
-+ (NSArray*)callStackSymbols {
-  return g_disable_callstacksymbols ?
-      @[@"+callStackSymbols disabled for performance reasons"] :
-      g_original_callstacksymbols_implementation(
-          self, @selector(callStackSymbols));
-}
-
-@end
-
-@implementation FullSizeContentWindow
-
-#pragma mark - Lifecycle
-
-// In initWithContentRect:styleMask:backing:defer:, the call to
-// [NSView addSubview:positioned:relativeTo:] causes NSWindow to complain that
-// an unknown view is being added to it, and to generate a stack trace.
-// Not only does this stack trace pollute the console, it can also take hundreds
-// of milliseconds to generate (because of symbolication). By swizzling
-// [NSThread callStackSymbols] we can prevent the stack trace output.
-// See crbug.com/520373 .
-+ (void)load {
-  // Swizzling should only happen in the browser process.
-  const char* const* const argv = *_NSGetArgv();
-  const int argc = *_NSGetArgc();
-  const char kType[] = "--type=";
-  for (int i = 1; i < argc; ++i) {
-    const char* arg = argv[i];
-    if (strncmp(arg, kType, strlen(kType)) == 0) {
-      return;
-    }
-  }
-
-  static dispatch_once_t onceToken;
-  dispatch_once(&onceToken, ^{
-    Class targetClass = [NSThread class];
-    Class swizzleClass = [FullSizeContentWindowSwizzlingSupport class];
-    SEL targetSelector = @selector(callStackSymbols);
-
-    CR_DEFINE_STATIC_LOCAL(base::mac::ScopedObjCClassSwizzler,
-                           callStackSymbolsSuppressor, (targetClass,
-                           swizzleClass, targetSelector));
-    g_original_callstacksymbols_implementation =
-        callStackSymbolsSuppressor.GetOriginalImplementation();
-  });
-}
-
-- (instancetype)init {
-  NOTREACHED();
-  return nil;
-}
-
-- (instancetype)initWithContentRect:(NSRect)contentRect
-                          styleMask:(NSUInteger)windowStyle
-                            backing:(NSBackingStoreType)bufferingType
-                              defer:(BOOL)deferCreation {
-  return [self initWithContentRect:contentRect
-                         styleMask:windowStyle
-                           backing:bufferingType
-                             defer:deferCreation
-            wantsViewsOverTitlebar:NO];
-}
-
-- (instancetype)initWithContentRect:(NSRect)contentRect
-                          styleMask:(NSUInteger)windowStyle
-                            backing:(NSBackingStoreType)bufferingType
-                              defer:(BOOL)deferCreation
-             wantsViewsOverTitlebar:(BOOL)wantsViewsOverTitlebar {
-  self = [super initWithContentRect:contentRect
-                          styleMask:windowStyle
-                            backing:bufferingType
-                              defer:deferCreation];
-  if (self) {
-    if (wantsViewsOverTitlebar &&
-        [FullSizeContentWindow
-            shouldUseFullSizeContentViewForStyle:windowStyle]) {
-      chromeWindowView_.reset([[FullSizeContentView alloc] init]);
-      [chromeWindowView_
-          setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
-      [self setContentView:chromeWindowView_];
-      [chromeWindowView_ setFrame:[[chromeWindowView_ superview] bounds]];
-
-      if (!chrome::ShouldUseFullSizeContentView()) {
-        // Chrome content view overlaps the window control buttons. Adding
-        // subview above the window's content view ensures that content view is
-        // positioned below the buttons.
-        NSView* superview = [chromeWindowView_ superview];
-        [chromeWindowView_ removeFromSuperview];
-
-        // Prevent the AppKit from generating a backtrace to include in it's
-        // complaint about our upcoming call to
-        // addSubview:positioned:relativeTo:. See +load for more info.
-        base::AutoReset<bool> disable_symbolication(&g_disable_callstacksymbols,
-                                                    true);
-
-        [superview addSubview:chromeWindowView_
-                   positioned:NSWindowBelow
-                   relativeTo:nil];
-      }
-    }
-  }
-  return self;
-}
-
-- (void)forceContentViewFrame:(NSRect)frame {
-  if ([chromeWindowView_ isKindOfClass:[FullSizeContentView class]]) {
-    FullSizeContentView* contentView =
-        base::mac::ObjCCast<FullSizeContentView>(chromeWindowView_);
-    [contentView forceFrame:frame];
-  } else if (chromeWindowView_) {
-    [chromeWindowView_ setFrame:frame];
-  } else {
-    [self.contentView setFrame:frame];
-  }
-}
-
-#pragma mark - Private Methods
-
-+ (BOOL)shouldUseFullSizeContentViewForStyle:(NSUInteger)windowStyle {
-  return windowStyle & NSTitledWindowMask;
-}
-
-#pragma mark - NSWindow Overrides
-
-+ (NSRect)frameRectForContentRect:(NSRect)cRect styleMask:(NSUInteger)aStyle {
-  if ([self shouldUseFullSizeContentViewForStyle:aStyle])
-    return cRect;
-  return [super frameRectForContentRect:cRect styleMask:aStyle];
-}
-
-- (NSRect)frameRectForContentRect:(NSRect)contentRect {
-  if (chromeWindowView_)
-    return contentRect;
-  return [super frameRectForContentRect:contentRect];
-}
-
-+ (NSRect)contentRectForFrameRect:(NSRect)fRect styleMask:(NSUInteger)aStyle {
-  if ([self shouldUseFullSizeContentViewForStyle:aStyle])
-    return fRect;
-  return [super contentRectForFrameRect:fRect styleMask:aStyle];
-}
-
-- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
-  if (chromeWindowView_)
-    return frameRect;
-  return [super contentRectForFrameRect:frameRect];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
index 11777d6..b42afba5 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
@@ -33,8 +33,6 @@
 // Uncommon icon that only shows on answer rows (e.g. weather).
 @property(readonly, retain, nonatomic) NSImage* answerImage;
 
-// The offset at which the tail suggestion contents should be displayed.
-@property(readonly, nonatomic) CGFloat contentsOffset;
 @property(readonly, nonatomic) BOOL isContentsRTL;
 
 // Is this suggestion an answer or calculator result.
@@ -43,7 +41,6 @@
 @property(readonly, nonatomic) int maxLines;
 
 - (instancetype)initWithMatch:(const AutocompleteMatch&)match
-               contentsOffset:(CGFloat)contentsOffset
                         image:(NSImage*)image
                   answerImage:(NSImage*)answerImage
                  forDarkTheme:(BOOL)isDarkTheme;
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
index 6f69d19..fd7cc66 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
@@ -366,10 +366,6 @@
                   origin:(NSPoint)origin
             withMaxWidth:(int)maxWidth
             forDarkTheme:(BOOL)isDarkTheme;
-- (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
-                          tableView:(OmniboxPopupMatrix*)tableView
-               withContentsMaxWidth:(int*)contentsMaxWidth
-                       forDarkTheme:(BOOL)isDarkTheme;
 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView;
 @end
 
@@ -381,21 +377,18 @@
 @synthesize image = image_;
 @synthesize incognitoImage = incognitoImage_;
 @synthesize answerImage = answerImage_;
-@synthesize contentsOffset = contentsOffset_;
 @synthesize isContentsRTL = isContentsRTL_;
 @synthesize isAnswer = isAnswer_;
 @synthesize matchType = matchType_;
 @synthesize maxLines = maxLines_;
 
 - (instancetype)initWithMatch:(const AutocompleteMatch&)match
-               contentsOffset:(CGFloat)contentsOffset
                         image:(NSImage*)image
                   answerImage:(NSImage*)answerImage
                  forDarkTheme:(BOOL)isDarkTheme {
   if ((self = [super init])) {
     image_ = [image retain];
     answerImage_ = [answerImage retain];
-    contentsOffset_ = contentsOffset;
 
     isContentsRTL_ =
         (base::i18n::RIGHT_TO_LEFT ==
@@ -523,14 +516,6 @@
   if (isVerticalLayout && descriptionMaxWidth == 0)
     origin.y += halfLineHeight;
 
-  if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
-    // Tail suggestions are rendered with a prefix (usually ellipsis), which
-    // appear vertically stacked.
-    origin.x += [self drawMatchPrefixWithFrame:cellFrame
-                                     tableView:tableView
-                          withContentsMaxWidth:&contentsMaxWidth
-                                  forDarkTheme:isDarkTheme];
-  }
   origin.x += [self drawMatchPart:[cellData contents]
                         withFrame:cellFrame
                            origin:origin
@@ -579,54 +564,6 @@
   }
 }
 
-- (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
-                          tableView:(OmniboxPopupMatrix*)tableView
-               withContentsMaxWidth:(int*)contentsMaxWidth
-                       forDarkTheme:(BOOL)isDarkTheme {
-  OmniboxPopupCellData* cellData =
-      base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]);
-  CGFloat offset = 0.0f;
-  CGFloat remainingWidth =
-      [OmniboxPopupCell getTextContentAreaWidth:[tableView contentMaxWidth]];
-  CGFloat prefixWidth = [[cellData prefix] size].width;
-
-  CGFloat prefixOffset = 0.0f;
-  if (base::i18n::IsRTL() != [cellData isContentsRTL]) {
-    // The contents is rendered between the contents offset extending towards
-    // the start edge, while prefix is rendered in opposite direction. Ideally
-    // the prefix should be rendered at |contentsOffset_|. If that is not
-    // sufficient to render the widest suggestion, we increase it to
-    // |maxMatchContentsWidth|.  If |remainingWidth| is not sufficient to
-    // accommodate that, we reduce the offset so that the prefix gets rendered.
-    prefixOffset = std::min(
-        remainingWidth - prefixWidth,
-        std::max([cellData contentsOffset], [tableView maxMatchContentsWidth]));
-    offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth);
-  } else { // The direction of contents is same as UI direction.
-    // Ideally the offset should be |contentsOffset_|. If the max total width
-    // (|prefixWidth| + |maxMatchContentsWidth|) from offset will exceed the
-    // |remainingWidth|, then we shift the offset to the left , so that all
-    // tail suggestions are visible.
-    // We have to render the prefix, so offset has to be at least |prefixWidth|.
-    offset =
-        std::max(prefixWidth,
-                 std::min(remainingWidth - [tableView maxMatchContentsWidth],
-                          [cellData contentsOffset]));
-    prefixOffset = offset - prefixWidth;
-  }
-  *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
-                               *contentsMaxWidth);
-  NSPoint origin = NSMakePoint(
-      prefixOffset + kMaterialTextStartOffset + [tableView contentLeftPadding],
-      0);
-  [self drawMatchPart:[cellData prefix]
-            withFrame:cellFrame
-               origin:origin
-         withMaxWidth:prefixWidth
-         forDarkTheme:isDarkTheme];
-  return offset;
-}
-
 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
                withFrame:(NSRect)cellFrame
                   origin:(NSPoint)origin
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
index 88eb7cd..77d6d340 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
@@ -44,7 +44,6 @@
   AutocompleteMatch match;
   cellData_.reset([[OmniboxPopupCellData alloc]
        initWithMatch:match
-      contentsOffset:0
                image:[NSImage imageNamed:NSImageNameInfo]
          answerImage:nil
         forDarkTheme:NO]);
@@ -57,7 +56,6 @@
   match.contents =
       base::ASCIIToUTF16("The quick brown fox jumps over the lazy dog.");
   cellData_.reset([[OmniboxPopupCellData alloc] initWithMatch:match
-                                               contentsOffset:0
                                                         image:nil
                                                   answerImage:nil
                                                  forDarkTheme:NO]);
@@ -84,7 +82,6 @@
   match.answer = SuggestionAnswer::ParseAnswer(dictionary);
   EXPECT_TRUE(match.answer);
   cellData_.reset([[OmniboxPopupCellData alloc] initWithMatch:match
-                                               contentsOffset:0
                                                         image:nil
                                                   answerImage:nil
                                                  forDarkTheme:NO]);
@@ -141,7 +138,6 @@
   match.answer = SuggestionAnswer::ParseAnswer(dictionary);
   EXPECT_TRUE(match.answer);
   cellData_.reset([[OmniboxPopupCellData alloc] initWithMatch:match
-                                               contentsOffset:0
                                                         image:nil
                                                   answerImage:nil
                                                  forDarkTheme:NO]);
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
index 93ec8a7..0b70e65 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
@@ -35,29 +35,18 @@
                          answerImage:(NSImage*)answerImage {
   base::scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
   BOOL isDarkTheme = [tableView hasDarkTheme];
-  CGFloat maxMatchContentsWidth = 0.0f;
-  CGFloat contentsOffset = -1.0f;
   for (const AutocompleteMatch& match : result) {
-    if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL &&
-        contentsOffset < 0.0f)
-      contentsOffset = [OmniboxPopupCell computeContentsOffset:match];
     base::scoped_nsobject<OmniboxPopupCellData> cellData(
         [[OmniboxPopupCellData alloc]
              initWithMatch:match
-            contentsOffset:contentsOffset
                      image:popupView.ImageForMatch(match)
                answerImage:(match.answer ? answerImage : nil)
               forDarkTheme:isDarkTheme]);
     if (isDarkTheme)
       [cellData setIncognitoImage:popupView.ImageForMatch(match)];
     [array addObject:cellData];
-    if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
-      maxMatchContentsWidth =
-          std::max(maxMatchContentsWidth, [cellData getMatchContentsWidth]);
-    }
   }
 
-  [tableView setMaxMatchContentsWidth:maxMatchContentsWidth];
   return [self initWithArray:array];
 }
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
index be6646d4..3b0ab4d2 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
@@ -87,6 +87,7 @@
 
 void OmniboxPopupViewMac::UpdatePopupAppearance() {
   DCHECK([NSThread isMainThread]);
+  model_->autocomplete_controller()->InlineTailPrefixes();
   const AutocompleteResult& result = GetResult();
   const size_t rows = result.size();
   if (rows == 0) {
diff --git a/chrome/browser/ui/cocoa/tabbed_browser_window.h b/chrome/browser/ui/cocoa/tabbed_browser_window.h
new file mode 100644
index 0000000..f1d11b2
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabbed_browser_window.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_TABBED_BROWSER_WINDOW_H_
+#define CHROME_BROWSER_UI_COCOA_TABBED_BROWSER_WINDOW_H_
+
+#import <AppKit/AppKit.h>
+
+#include "chrome/browser/ui/cocoa/framed_browser_window.h"
+
+// Represents a browser window with tabs and customized window controls.
+@interface TabbedBrowserWindow : FramedBrowserWindow
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_TABBED_BROWSER_WINDOW_H_
diff --git a/chrome/browser/ui/cocoa/tabbed_browser_window.mm b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
new file mode 100644
index 0000000..ce42d44
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
@@ -0,0 +1,207 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/tabbed_browser_window.h"
+
+#import "chrome/browser/ui/cocoa/browser_window_controller.h"
+#import "chrome/browser/ui/cocoa/browser_window_layout.h"
+
+// Implementer's note: Moving the window controls is tricky. When altering the
+// code, ensure that:
+// - accessibility hit testing works
+// - the accessibility hierarchy is correct
+// - close/min in the background don't bring the window forward
+// - rollover effects work correctly
+
+namespace {
+// Size of the gradient. Empirically determined so that the gradient looks
+// like what the heuristic does when there are just a few tabs.
+constexpr CGFloat kWindowGradientHeight = 24.0;
+
+// Offsets from the bottom/left of the titlebar to the bottom/left of the
+// window buttons (zoom, close, miniaturize).
+constexpr NSInteger kWindowButtonsOffsetFromBottom = 9;
+constexpr NSInteger kWindowButtonsOffsetFromLeft = 11;
+
+// Offset from the top/right of the window to the Mavericks full screen button.
+constexpr NSInteger kFullScreenButtonOffset = 9;
+}  // namespace
+
+@interface TabbedBrowserWindow ()
+- (CGFloat)fullScreenButtonOriginAdjustment;
+@end
+
+// Weak so that Chrome will launch if a future macOS doesn't have NSThemeFrame.
+WEAK_IMPORT_ATTRIBUTE
+@interface NSThemeFrame : NSView
+- (NSView*)fullScreenButton
+    __attribute__((availability(macos, obsoleted = 10.10)));
+@end
+
+@interface NSWindow (PrivateAPI)
++ (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle;
+@end
+
+@interface NSWindow (TenTwelveSDK)
+@property(readonly)
+    NSUserInterfaceLayoutDirection windowTitlebarLayoutDirection;
+@end
+
+@interface TabbedBrowserWindowFrame : NSThemeFrame
+@end
+
+@implementation TabbedBrowserWindowFrame
+
+// NSThemeFrame overrides.
+
+- (CGFloat)_minXTitlebarWidgetInset {
+  return kWindowButtonsOffsetFromLeft;
+}
+
+- (CGFloat)_minYTitlebarButtonsOffset {
+  return -kWindowButtonsOffsetFromBottom;
+}
+
+- (CGFloat)_titlebarHeight {
+  return chrome::kTabStripHeight;
+}
+
+// AppKit's implementation only returns YES if [self class] == [NSThemeFrame
+// class]. TabbedBrowserWindowFrame could override -class to return that, but
+// then -[NSWindow setStyleMask:] would recreate the frame view each time the
+// style mask is touched because it wouldn't match the return value of
+// +[TabbedBrowserWindow frameViewClassForStyleMask:].
+- (BOOL)_shouldFlipTrafficLightsForRTL API_AVAILABLE(macos(10.12)) {
+  return [[self window] windowTitlebarLayoutDirection] ==
+         NSUserInterfaceLayoutDirectionRightToLeft;
+}
+
+@end
+
+// By default, contentView does not occupy the full size of a titled window,
+// and Chrome wants to draw in the title bar. Historically, Chrome did this by
+// adding subviews directly to the root view. This causes several problems. The
+// most egregious is related to layer ordering when the root view does not have
+// a layer. By giving the contentView the same size as the window, there is no
+// need to add subviews to the root view.
+//
+// TODO(sdy): This can be deleted once ShouldUseFullSizeContentView is
+// perma-on. See https://crbug.com/605219.
+@interface FullSizeTabbedBrowserWindowFrame : TabbedBrowserWindowFrame
+@end
+
+@implementation FullSizeTabbedBrowserWindowFrame
+
++ (CGRect)contentRectForFrameRect:(CGRect)frameRect
+                        styleMask:(NSUInteger)style {
+  return frameRect;
+}
+
++ (CGRect)frameRectForContentRect:(CGRect)contentRect
+                        styleMask:(NSUInteger)style {
+  return contentRect;
+}
+
+- (CGRect)contentRectForFrameRect:(CGRect)frameRect
+                        styleMask:(NSUInteger)style {
+  return frameRect;
+}
+
+- (CGRect)frameRectForContentRect:(CGRect)contentRect
+                        styleMask:(NSUInteger)style {
+  return contentRect;
+}
+
+@end
+
+@interface MavericksTabbedBrowserWindowFrame : FullSizeTabbedBrowserWindowFrame
+@end
+
+@implementation MavericksTabbedBrowserWindowFrame
+
+// 10.10+ has no separate fullscreen button.
+- (NSPoint)_fullScreenButtonOrigin
+    __attribute__((availability(macos, obsoleted = 10.10))) {
+  CGFloat xAdjustment = [static_cast<TabbedBrowserWindow*>(self.window)
+      fullScreenButtonOriginAdjustment];
+  NSSize fullScreenButtonSize = [self fullScreenButton].frame.size;
+  return NSMakePoint(NSMaxX(self.bounds) - fullScreenButtonSize.width -
+                         kFullScreenButtonOffset - xAdjustment,
+                     NSMaxY(self.bounds) - fullScreenButtonSize.height -
+                         kFullScreenButtonOffset);
+}
+
+// 10.10+ adds the content view below the window controls by default.
+- (void)_setContentView:(NSView*)contentView {
+  [self addSubview:contentView positioned:NSWindowBelow relativeTo:nil];
+}
+
+@end
+
+@implementation TabbedBrowserWindow
+
+// FramedBrowserWindow overrides.
+
++ (NSUInteger)defaultStyleMask {
+  NSUInteger styleMask = [super defaultStyleMask];
+  if (chrome::ShouldUseFullSizeContentView()) {
+    if (@available(macOS 10.10, *))
+      styleMask |= NSFullSizeContentViewWindowMask;
+  }
+  return styleMask;
+}
+
+// NSWindow (PrivateAPI) overrides.
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle {
+  // Because NSThemeFrame is imported weakly, if it's not present at runtime
+  // then it and its subclasses will be nil.
+  if ([TabbedBrowserWindowFrame class]) {
+    if (@available(macOS 10.10, *)) {
+      return chrome::ShouldUseFullSizeContentView()
+                 ? [TabbedBrowserWindowFrame class]
+                 : [FullSizeTabbedBrowserWindowFrame class];
+    }
+    return [MavericksTabbedBrowserWindowFrame class];
+  }
+  return [super frameViewClassForStyleMask:windowStyle];
+}
+
+// NSWindow's implementation of _usesCustomDrawing returns YES when the window
+// has a custom frame view class, which causes several undesirable changes in
+// AppKit's behavior. NSWindow subclasses in AppKit override it and return NO.
+- (BOOL)_usesCustomDrawing {
+  return NO;
+}
+
+// FramedBrowserWindow overrides.
+
+- (id)initWithContentRect:(NSRect)contentRect {
+  if ((self = [super initWithContentRect:contentRect])) {
+    // The following two calls fix http://crbug.com/25684 by preventing the
+    // window from recalculating the border thickness as the window is
+    // resized.
+    // This was causing the window tint to change for the default system theme
+    // when the window was being resized.
+    [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
+    [self setContentBorderThickness:kWindowGradientHeight forEdge:NSMaxYEdge];
+  }
+  return self;
+}
+
+// TabbedBrowserWindow () implementation.
+
+- (CGFloat)fullScreenButtonOriginAdjustment {
+  // If there is a profile avatar icon present, shift the button over by its
+  // width and some padding. The new avatar button is displayed to the right
+  // of the fullscreen icon, so it doesn't need to be shifted.
+  auto* bwc = static_cast<BrowserWindowController*>([self windowController]);
+  if ([bwc shouldShowAvatar] && ![bwc shouldUseNewAvatarButton]) {
+    NSView* avatarButton = [[bwc avatarButtonController] view];
+    return NSWidth([avatarButton frame]) - 3;
+  }
+  return 0;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/tabbed_browser_window_unittest.mm b/chrome/browser/ui/cocoa/tabbed_browser_window_unittest.mm
new file mode 100644
index 0000000..e889127
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabbed_browser_window_unittest.mm
@@ -0,0 +1,138 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <AppKit/AppKit.h>
+
+#import "chrome/browser/ui/cocoa/tabbed_browser_window.h"
+#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
+
+namespace {
+NSString* const kAppleTextDirectionDefaultsKey = @"AppleTextDirection";
+NSString* const kForceRTLWritingDirectionDefaultsKey =
+    @"NSForceRightToLeftWritingDirection";
+constexpr CGFloat kWindowButtonInset = 11;
+}  // namespace
+
+class TabbedBrowserWindowTest : public CocoaTest {
+ protected:
+  TabbedBrowserWindowTest() = default;
+
+  void SetUp() override {
+    CocoaTest::SetUp();
+    window_ = [[TabbedBrowserWindow alloc]
+        initWithContentRect:NSMakeRect(0, 0, 800, 600)];
+    [window() orderBack:nil];
+  }
+
+  void TearDown() override {
+    [window() close];
+    CocoaTest::TearDown();
+  }
+
+  TabbedBrowserWindow* window() { return window_; };
+
+ private:
+  TabbedBrowserWindow* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(TabbedBrowserWindowTest);
+};
+
+// Test to make sure that our window widgets are in the right place.
+TEST_F(TabbedBrowserWindowTest, WindowWidgetLocation) {
+  NSView* closeBoxControl = [window() standardWindowButton:NSWindowCloseButton];
+  EXPECT_TRUE(closeBoxControl);
+  NSRect closeBoxFrame =
+      [closeBoxControl convertRect:[closeBoxControl bounds] toView:nil];
+  NSRect windowBounds = [window() frame];
+  windowBounds = [[window() contentView] convertRect:windowBounds fromView:nil];
+  windowBounds.origin = NSZeroPoint;
+  EXPECT_EQ(NSMaxY(closeBoxFrame), NSMaxY(windowBounds) - kWindowButtonInset);
+  EXPECT_EQ(NSMinX(closeBoxFrame), kWindowButtonInset);
+}
+
+class TabbedBrowserWindowRTLTest : public TabbedBrowserWindowTest {
+ public:
+  TabbedBrowserWindowRTLTest() = default;
+
+  void SetUp() override {
+    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+    originalAppleTextDirection_ =
+        [defaults boolForKey:kAppleTextDirectionDefaultsKey];
+    originalRTLWritingDirection_ =
+        [defaults boolForKey:kForceRTLWritingDirectionDefaultsKey];
+    [defaults setBool:YES forKey:kAppleTextDirectionDefaultsKey];
+    [defaults setBool:YES forKey:kForceRTLWritingDirectionDefaultsKey];
+    TabbedBrowserWindowTest::SetUp();
+  }
+
+  void TearDown() override {
+    TabbedBrowserWindowTest::TearDown();
+    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setBool:originalAppleTextDirection_
+               forKey:kAppleTextDirectionDefaultsKey];
+    [defaults setBool:originalRTLWritingDirection_
+               forKey:kForceRTLWritingDirectionDefaultsKey];
+  }
+
+ private:
+  BOOL originalAppleTextDirection_;
+  BOOL originalRTLWritingDirection_;
+
+  DISALLOW_COPY_AND_ASSIGN(TabbedBrowserWindowRTLTest);
+};
+
+// Test to make sure that our window widgets are in the right place.
+// Currently, this is the same exact test as above, since RTL button
+// layout is behind the ExperimentalMacRTL flag. However, this ensures
+// that our calculations are correct for Sierra RTL, which lays out
+// the window buttons in reverse by default. See crbug/662079.
+TEST_F(TabbedBrowserWindowRTLTest, WindowWidgetLocation) {
+  NSView* closeBoxControl = [window() standardWindowButton:NSWindowCloseButton];
+  EXPECT_TRUE(closeBoxControl);
+  NSRect closeBoxFrame =
+      [closeBoxControl convertRect:[closeBoxControl bounds] toView:nil];
+  NSRect windowBounds = [window() frame];
+  windowBounds = [[window() contentView] convertRect:windowBounds fromView:nil];
+  windowBounds.origin = NSZeroPoint;
+  EXPECT_EQ(NSMaxY(closeBoxFrame), NSMaxY(windowBounds) - kWindowButtonInset);
+  EXPECT_EQ(NSMinX(closeBoxFrame), kWindowButtonInset);
+}
+
+WEAK_IMPORT_ATTRIBUTE
+@interface NSThemeFrame : NSView
+@end
+
+// Test that NSThemeFrame and NSWindow respond to the undocumented methods
+// which TabbedBrowserWindowFrame and TabbedBrowserWindow override.
+TEST(TabbedBrowserWindowOverrideTest, UndocumentedMethods) {
+  EXPECT_TRUE([NSThemeFrame
+      respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]);
+  EXPECT_TRUE([NSThemeFrame
+      respondsToSelector:@selector(frameRectForContentRect:styleMask:)]);
+  EXPECT_TRUE([NSThemeFrame
+      instancesRespondToSelector:@selector(_minXTitlebarWidgetInset)]);
+  EXPECT_TRUE([NSThemeFrame
+      instancesRespondToSelector:@selector(_minYTitlebarButtonsOffset)]);
+  EXPECT_TRUE(
+      [NSThemeFrame instancesRespondToSelector:@selector(_titlebarHeight)]);
+  EXPECT_TRUE([NSThemeFrame instancesRespondToSelector:@selector
+                            (contentRectForFrameRect:styleMask:)]);
+  EXPECT_TRUE([NSThemeFrame instancesRespondToSelector:@selector
+                            (frameRectForContentRect:styleMask:)]);
+  if (@available(macOS 10.12, *)) {
+    EXPECT_TRUE([NSThemeFrame
+        instancesRespondToSelector:@selector(_shouldFlipTrafficLightsForRTL)]);
+  } else if (@available(macOS 10.10, *)) {
+  } else {
+    EXPECT_TRUE([NSThemeFrame
+        instancesRespondToSelector:@selector(_fullScreenButtonOrigin)]);
+    EXPECT_TRUE(
+        [NSThemeFrame instancesRespondToSelector:@selector(_setContentView:)]);
+  }
+
+  EXPECT_TRUE(
+      [NSWindow respondsToSelector:@selector(frameViewClassForStyleMask:)]);
+  EXPECT_TRUE(
+      [NSWindow instancesRespondToSelector:@selector(_usesCustomDrawing)]);
+}
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
index 9d76445..bcec69f 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
@@ -9,6 +9,7 @@
 #import "chrome/browser/ui/cocoa/browser_window_layout.h"
 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
+#import "chrome/browser/ui/cocoa/tabbed_browser_window.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
@@ -21,9 +22,7 @@
 - (void)setUseOverlay:(BOOL)useOverlay;
 
 // The tab strip background view should always be inserted as the back-most
-// subview of the root view. It cannot be a subview of the contentView, as that
-// would cause it to become layer backed, which would cause it to draw on top
-// of non-layer backed content like the window controls.
+// subview of the contentView.
 - (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window
                                       titleBar:(BOOL)hasTitleBar;
 
@@ -93,8 +92,8 @@
 
   NSRect contentRect = NSMakeRect(60, 229, kDefaultWidth, kDefaultHeight);
   base::scoped_nsobject<FramedBrowserWindow> window(
-      [[FramedBrowserWindow alloc] initWithContentRect:contentRect
-                                           hasTabStrip:hasTabStrip]);
+      [(hasTabStrip ? [TabbedBrowserWindow alloc] : [FramedBrowserWindow alloc])
+          initWithContentRect:contentRect]);
   [window setReleasedWhenClosed:YES];
   [window setAutorecalculatesKeyViewLoop:YES];
 
@@ -423,7 +422,6 @@
 - (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window
                                       titleBar:(BOOL)hasTitleBar {
   DCHECK(tabStripBackgroundView_);
-  NSView* rootView = [[window contentView] superview];
 
   // In Material Design on 10.10 and higher, the top portion of the window is
   // blurred using an NSVisualEffectView.
@@ -467,20 +465,18 @@
 
     [visualEffectWrapperView addSubview:visualEffectView_];
 
-    [chrome::ShouldUseFullSizeContentView() ? [window contentView] : rootView
-        addSubview:visualEffectWrapperView
-        positioned:NSWindowBelow
-        relativeTo:nil];
+    [[window contentView] addSubview:visualEffectWrapperView
+                          positioned:NSWindowBelow
+                          relativeTo:nil];
 
     // Make the |tabStripBackgroundView_| a child of the NSVisualEffectView.
     [tabStripBackgroundView_ setFrame:[visualEffectView_ bounds]];
     [visualEffectView_ addSubview:tabStripBackgroundView_];
   } else {
     DCHECK(!chrome::ShouldUseFullSizeContentView());
-    [rootView addSubview:tabStripBackgroundView_
-              positioned:NSWindowBelow
-              relativeTo:nil];
-    return;
+    [[window contentView] addSubview:tabStripBackgroundView_
+                          positioned:NSWindowBelow
+                          relativeTo:nil];
   }
 }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
index 6e93bce..7b94664 100644
--- a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
@@ -50,17 +50,26 @@
   // Complete the Payment Request.
   PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
 
-  // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.PayClicked", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", 1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Completed",
-                                      1, 1);
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -79,13 +88,30 @@
   WaitForObservedEvent();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -106,13 +132,30 @@
   WaitForObservedEvent();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -134,13 +177,30 @@
   WaitForObservedEvent();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -156,13 +216,30 @@
   NavigateTo("/payment_request_email_test.html");
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_USER_NAVIGATION, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -178,13 +255,30 @@
   ClickOnCancel();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_ABORTED_BY_USER, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -202,13 +296,30 @@
   WaitForObservedEvent();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_ABORTED_BY_USER, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompletionStatusMetricsTest,
@@ -226,13 +337,30 @@
   WaitForObservedEvent();
 
   // Make sure the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
   histogram_tester.ExpectUniqueSample(
       "PaymentRequest.CheckoutFunnel.Aborted",
       JourneyLogger::ABORT_REASON_USER_NAVIGATION, 1);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
 }
 
 class PaymentRequestInitiatedCompletionStatusMetricsTest
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index 7105bf70..1a8f9dc 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -44,14 +44,6 @@
   ResetEventObserver(DialogEvent::DIALOG_CLOSED);
   PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
 
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.PayClicked", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", 1, 1);
   // Expect a credit card as the selected payment instrument in the metrics.
   histogram_tester.ExpectBucketCount(
       "PaymentRequest.SelectedPaymentMethod",
@@ -77,6 +69,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 class PaymentRequestJourneyLoggerNoSupportedPaymentMethodTest
@@ -101,8 +95,6 @@
   ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
   WaitForObservedEvent();
 
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
   histogram_tester.ExpectBucketCount(
       "PaymentRequest.CheckoutFunnel.NoShow",
       JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD, 1);
@@ -151,18 +143,6 @@
       histogram_tester.GetAllSamples("PaymentRequest.CheckoutFunnel.NoShow")
           .empty());
 
-  // Expect that other metrics were logged correctly.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.PayClicked", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", 1, 1);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Completed",
-                                      1, 1);
-
   // Make sure the correct events were logged.
   std::vector<base::Bucket> buckets =
       histogram_tester.GetAllSamples("PaymentRequest.Events");
@@ -183,6 +163,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerMultipleShowTest,
@@ -215,19 +197,7 @@
   // Complete the original Payment Request.
   PayWithCreditCardAndWait(base::ASCIIToUTF16("123"), first_dialog_view);
 
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 2);
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Shown", 1,
-                                      1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.PayClicked", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", 1, 1);
-
-  // The metrics should show that the original Payment Request should be
-  // completed and the second one should not have been shown.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Completed",
-                                      1, 1);
+  // There is one no show and one shown (verified below).
   histogram_tester.ExpectBucketCount(
       "PaymentRequest.CheckoutFunnel.NoShow",
       JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS, 1);
@@ -252,6 +222,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 class PaymentRequestJourneyLoggerAllSectionStatsTest
@@ -315,6 +287,8 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -367,6 +341,8 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 class PaymentRequestJourneyLoggerNoShippingSectionStatsTest
@@ -432,6 +408,8 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -485,6 +463,8 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 class PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest
@@ -552,6 +532,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -607,6 +589,10 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 class PaymentRequestNotShownTest : public PaymentRequestBrowserTestBase {
@@ -628,11 +614,6 @@
   // Navigate away to abort the Payment Request and trigger the logs.
   NavigateTo("/payment_request_email_test.html");
 
-  // Initiated should be logged.
-  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
-                                      1, 1);
-  // Show should not be logged.
-  histogram_tester.ExpectTotalCount("PaymentRequest.CheckoutFunnel.Shown", 0);
   // Abort should not be logged.
   histogram_tester.ExpectTotalCount("PaymentRequest.CheckoutFunnel.Aborted", 0);
 
@@ -721,6 +702,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompleteSuggestionsForEverythingTest,
@@ -766,6 +749,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -817,6 +802,8 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index aef5d2b..09b0068 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -206,6 +206,7 @@
     offline_page->SetDouble("lastAccessTime", page.last_access_time.ToJsTime());
     offline_page->SetInteger("accessCount", page.access_count);
     offline_page->SetString("originalUrl", page.original_url.spec());
+    offline_page->SetString("requestOrigin", page.request_origin);
     results.Append(std::move(offline_page));
   }
   ResolveJavascriptCallback(base::Value(callback_id), results);
@@ -230,6 +231,7 @@
       save_page_request->SetString("id", std::to_string(request->request_id()));
       save_page_request->SetString("originalUrl",
                                    request->original_url().spec());
+      save_page_request->SetString("requestOrigin", request->request_origin());
       save_page_requests.Append(std::move(save_page_request));
     }
   }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3f389ce..4429362 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1427,6 +1427,7 @@
       "../browser/sessions/persistent_tab_restore_service_browsertest.cc",
       "../browser/sessions/session_restore_browsertest.cc",
       "../browser/sessions/session_restore_browsertest_chromeos.cc",
+      "../browser/sessions/session_restore_observer_browsertest.cc",
       "../browser/sessions/tab_restore_browsertest.cc",
       "../browser/site_details_browsertest.cc",
 
@@ -4633,7 +4634,6 @@
         "../browser/ui/cocoa/content_settings/collected_cookies_mac_unittest.mm",
         "../browser/ui/cocoa/content_settings/cookie_details_unittest.mm",
         "../browser/ui/cocoa/content_settings/cookie_details_view_controller_unittest.mm",
-        "../browser/ui/cocoa/custom_frame_view_unittest.mm",
         "../browser/ui/cocoa/download/download_item_button_unittest.mm",
         "../browser/ui/cocoa/download/download_item_cell_unittest.mm",
         "../browser/ui/cocoa/download/download_item_controller_unittest.mm",
@@ -4729,6 +4729,7 @@
         "../browser/ui/cocoa/styled_text_field_cell_unittest.mm",
         "../browser/ui/cocoa/styled_text_field_unittest.mm",
         "../browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm",
+        "../browser/ui/cocoa/tabbed_browser_window_unittest.mm",
         "../browser/ui/cocoa/tabs/alert_indicator_button_cocoa_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_controller_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_strip_controller_unittest.mm",
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index d135f25a..506cb34 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -21,7 +21,6 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "cc/base/switches.h"
 #include "chromecast/base/cast_constants.h"
 #include "chromecast/base/cast_features.h"
 #include "chromecast/base/cast_paths.h"
@@ -66,6 +65,7 @@
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "media/base/media.h"
 #include "media/base/media_switches.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/compositor/compositor_switches.h"
 #include "ui/gl/gl_switches.h"
 
@@ -264,7 +264,7 @@
     // TODO(halliwell): Remove after fixing b/35422666.
     {switches::kEnableUseZoomForDSF, "false"},
     // TODO(halliwell): Revert after fix for b/63101386.
-    {cc::switches::kDisallowNonExactResourceReuse, ""},
+    {switches::kDisallowNonExactResourceReuse, ""},
 };
 
 void AddDefaultCommandLineSwitches(base::CommandLine* command_line) {
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index 6f6d64f0..f57b008 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -133,13 +133,11 @@
 }
 
 void JourneyLogger::SetCompleted() {
-  UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Completed", true);
-
   RecordJourneyStatsHistograms(COMPLETION_STATUS_COMPLETED);
 }
 
 void JourneyLogger::SetAborted(AbortReason reason) {
-  // Don't log abort reasons if the Payment Request was triggered.
+  // Don't log abort reasons if the Payment Request was not triggered.
   if (WasPaymentRequestTriggered()) {
     base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.Aborted",
                                   reason, ABORT_REASON_MAX);
@@ -155,10 +153,6 @@
 void JourneyLogger::SetNotShown(NotShownReason reason) {
   base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.NoShow", reason,
                                 NOT_SHOWN_REASON_MAX);
-
-  // Record that that Payment Request was initiated here, because nothing else
-  // will be recorded for a Payment Request that was not shown to the user.
-  UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
 }
 
 void JourneyLogger::RecordJourneyStatsHistograms(
@@ -166,7 +160,6 @@
   DCHECK(!has_recorded_);
   has_recorded_ = true;
 
-  RecordCheckoutFlowMetrics();
   RecordCanMakePaymentStats(completion_status);
   RecordUrlKeyedMetrics(completion_status);
   RecordEventsMetric(completion_status);
@@ -179,23 +172,6 @@
   }
 }
 
-void JourneyLogger::RecordCheckoutFlowMetrics() {
-  UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
-
-  if (events_ & EVENT_SHOWN)
-    UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Shown", true);
-
-  if (events_ & EVENT_PAY_CLICKED)
-    UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.PayClicked", true);
-
-  if (events_ & EVENT_RECEIVED_INSTRUMENT_DETAILS)
-    UMA_HISTOGRAM_BOOLEAN(
-        "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", true);
-
-  if (events_ & EVENT_SKIPPED_SHOW)
-    UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.SkippedShow", true);
-}
-
 void JourneyLogger::RecordPaymentMethodMetric() {
   base::UmaHistogramEnumeration("PaymentRequest.SelectedPaymentMethod",
                                 payment_method_, SELECTED_PAYMENT_METHOD_MAX);
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index bb254dae..f7665b1 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -219,10 +219,6 @@
   // either been completed or aborted.
   void RecordJourneyStatsHistograms(CompletionStatus completion_status);
 
-  // Records the histograms for all the steps of a complete checkout flow that
-  // were reached.
-  void RecordCheckoutFlowMetrics();
-
   // Records the metric about the selected payment method.
   void RecordPaymentMethodMetric();
 
diff --git a/components/search_provider_logos/logo_service.cc b/components/search_provider_logos/logo_service.cc
index f3b6fd2..b5d98989 100644
--- a/components/search_provider_logos/logo_service.cc
+++ b/components/search_provider_logos/logo_service.cc
@@ -108,13 +108,17 @@
 LogoService::~LogoService() = default;
 
 void LogoService::GetLogo(search_provider_logos::LogoObserver* observer) {
-  if (!template_url_service_)
+  if (!template_url_service_) {
+    observer->OnObserverRemoved();
     return;
+  }
 
   const TemplateURL* template_url =
       template_url_service_->GetDefaultSearchProvider();
-  if (!template_url)
+  if (!template_url) {
+    observer->OnObserverRemoved();
     return;
+  }
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   GURL logo_url;
@@ -129,6 +133,7 @@
   if (!template_url->url_ref().HasGoogleBaseURLs(
           template_url_service_->search_terms_data()) &&
       !use_fixed_logo) {
+    observer->OnObserverRemoved();
     return;
   }
 
diff --git a/components/viz/DEPS b/components/viz/DEPS
index c4d836f..09630076 100644
--- a/components/viz/DEPS
+++ b/components/viz/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "-components/viz",
   "+components/viz/common",
+  "+ui/base",
 ]
diff --git a/components/viz/host/BUILD.gn b/components/viz/host/BUILD.gn
index 6e3abfe..092768f 100644
--- a/components/viz/host/BUILD.gn
+++ b/components/viz/host/BUILD.gn
@@ -11,6 +11,8 @@
     "host_frame_sink_client.h",
     "host_frame_sink_manager.cc",
     "host_frame_sink_manager.h",
+    "renderer_settings_creation.cc",
+    "renderer_settings_creation.h",
     "server_gpu_memory_buffer_manager.cc",
     "server_gpu_memory_buffer_manager.h",
     "viz_host_export.h",
@@ -22,6 +24,8 @@
     "//gpu/ipc/client",
     "//gpu/ipc/common",
     "//services/ui/gpu/interfaces",
+    "//ui/base",
+    "//ui/gfx",
 
     # TODO(kylechar): This is temporary and will be removed when all host to
     # service communication is over Mojo.
diff --git a/ui/compositor/compositor_util.cc b/components/viz/host/renderer_settings_creation.cc
similarity index 61%
rename from ui/compositor/compositor_util.cc
rename to components/viz/host/renderer_settings_creation.cc
index d0f5026..8f922a7c 100644
--- a/ui/compositor/compositor_util.cc
+++ b/components/viz/host/renderer_settings_creation.cc
@@ -2,27 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/compositor/compositor_util.h"
+#include "components/viz/host/renderer_settings_creation.h"
 
 #include "base/command_line.h"
-#include "cc/base/switches.h"
+#include "base/feature_list.h"
+#include "build/build_config.h"
 #include "components/viz/common/display/renderer_settings.h"
-#include "ui/compositor/compositor_switches.h"
-#include "ui/display/display_switches.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/gfx/color_space_switches.h"
 
-namespace ui {
+namespace viz {
 
-viz::ResourceSettings CreateResourceSettings(
-    const viz::BufferToTextureTargetMap& image_targets) {
-  viz::ResourceSettings resource_settings;
+ResourceSettings CreateResourceSettings(
+    const BufferToTextureTargetMap& image_targets) {
+  ResourceSettings resource_settings;
   resource_settings.buffer_to_texture_target_map = image_targets;
   return resource_settings;
 }
 
-viz::RendererSettings CreateRendererSettings(
-    const viz::BufferToTextureTargetMap& image_targets) {
-  viz::RendererSettings renderer_settings;
+RendererSettings CreateRendererSettings(
+    const BufferToTextureTargetMap& image_targets) {
+  RendererSettings renderer_settings;
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   renderer_settings.partial_swap_enabled =
       !command_line->HasSwitch(switches::kUIDisablePartialSwap);
@@ -33,21 +33,21 @@
 #endif
   renderer_settings.gl_composited_overlay_candidate_quad_border =
       command_line->HasSwitch(
-          cc::switches::kGlCompositedOverlayCandidateQuadBorder);
+          switches::kGlCompositedOverlayCandidateQuadBorder);
   renderer_settings.show_overdraw_feedback =
-      command_line->HasSwitch(cc::switches::kShowOverdrawFeedback);
+      command_line->HasSwitch(switches::kShowOverdrawFeedback);
   renderer_settings.enable_color_correct_rendering =
       base::FeatureList::IsEnabled(features::kColorCorrectRendering);
   renderer_settings.resource_settings = CreateResourceSettings(image_targets);
   renderer_settings.show_overdraw_feedback =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          cc::switches::kShowOverdrawFeedback);
+          switches::kShowOverdrawFeedback);
   renderer_settings.disallow_non_exact_resource_reuse =
-      command_line->HasSwitch(cc::switches::kDisallowNonExactResourceReuse);
+      command_line->HasSwitch(switches::kDisallowNonExactResourceReuse);
   renderer_settings.allow_antialiasing =
-      !command_line->HasSwitch(cc::switches::kDisableCompositedAntialiasing);
+      !command_line->HasSwitch(switches::kDisableCompositedAntialiasing);
 
   return renderer_settings;
 }
 
-}  // namespace ui
+}  // namespace viz
diff --git a/components/viz/host/renderer_settings_creation.h b/components/viz/host/renderer_settings_creation.h
new file mode 100644
index 0000000..3c8459a
--- /dev/null
+++ b/components/viz/host/renderer_settings_creation.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_HOST_RENDERER_SETTINGS_CREATION_H_
+#define COMPONENTS_VIZ_HOST_RENDERER_SETTINGS_CREATION_H_
+
+#include <stdint.h>
+
+#include "components/viz/common/resources/buffer_to_texture_target_map.h"
+#include "components/viz/host/viz_host_export.h"
+
+namespace viz {
+class RendererSettings;
+class ResourceSettings;
+}  // namespace viz
+
+namespace viz {
+
+// |image_targets| is a map from every supported pair of GPU memory buffer
+// usage/format to its GL texture target.
+VIZ_HOST_EXPORT ResourceSettings
+CreateResourceSettings(const BufferToTextureTargetMap& image_targets);
+
+VIZ_HOST_EXPORT RendererSettings
+CreateRendererSettings(const BufferToTextureTargetMap& image_targets);
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_HOST_RENDERER_SETTINGS_CREATION_H_
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 5edb4bb..52bd20e 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -47,6 +47,8 @@
     "display_embedder/server_shared_bitmap_manager.h",
     "display_embedder/shared_bitmap_allocation_notifier_impl.cc",
     "display_embedder/shared_bitmap_allocation_notifier_impl.h",
+    "frame_sinks/compositor_frame_sink_impl.cc",
+    "frame_sinks/compositor_frame_sink_impl.h",
     "frame_sinks/compositor_frame_sink_support.cc",
     "frame_sinks/compositor_frame_sink_support.h",
     "frame_sinks/compositor_frame_sink_support_client.h",
@@ -60,14 +62,12 @@
     "frame_sinks/frame_sink_manager_client.h",
     "frame_sinks/frame_sink_manager_impl.cc",
     "frame_sinks/frame_sink_manager_impl.h",
-    "frame_sinks/gpu_compositor_frame_sink.cc",
-    "frame_sinks/gpu_compositor_frame_sink.h",
-    "frame_sinks/gpu_root_compositor_frame_sink.cc",
-    "frame_sinks/gpu_root_compositor_frame_sink.h",
     "frame_sinks/primary_begin_frame_source.cc",
     "frame_sinks/primary_begin_frame_source.h",
     "frame_sinks/referenced_surface_tracker.cc",
     "frame_sinks/referenced_surface_tracker.h",
+    "frame_sinks/root_compositor_frame_sink_impl.cc",
+    "frame_sinks/root_compositor_frame_sink_impl.h",
     "frame_sinks/surface_resource_holder.cc",
     "frame_sinks/surface_resource_holder.h",
     "frame_sinks/surface_resource_holder_client.h",
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 9f4ca42..e59a33f 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/service/image_factory.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+#include "ui/base/ui_base_switches.h"
 
 #if defined(USE_OZONE)
 #include "components/viz/service/display_embedder/display_output_surface_ozone.h"
@@ -93,7 +94,6 @@
       synthetic_begin_frame_source.get(), task_runner_.get(),
       max_frames_pending);
 
-
   // The ownership of the BeginFrameSource is transfered to the caller.
   *begin_frame_source = std::move(synthetic_begin_frame_source);
 
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc
new file mode 100644
index 0000000..d5fbdd5
--- /dev/null
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/compositor_frame_sink_impl.h"
+
+#include <utility>
+
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+CompositorFrameSinkImpl::CompositorFrameSinkImpl(
+    FrameSinkManagerImpl* frame_sink_manager,
+    const FrameSinkId& frame_sink_id,
+    mojom::CompositorFrameSinkRequest request,
+    mojom::CompositorFrameSinkClientPtr client)
+    : support_(
+          CompositorFrameSinkSupport::Create(this,
+                                             frame_sink_manager,
+                                             frame_sink_id,
+                                             false /* is_root */,
+                                             true /* needs_sync_points */)),
+      client_(std::move(client)),
+      compositor_frame_sink_binding_(this, std::move(request)) {
+  compositor_frame_sink_binding_.set_connection_error_handler(
+      base::Bind(&CompositorFrameSinkImpl::OnClientConnectionLost,
+                 base::Unretained(this)));
+}
+
+CompositorFrameSinkImpl::~CompositorFrameSinkImpl() = default;
+
+void CompositorFrameSinkImpl::SetNeedsBeginFrame(bool needs_begin_frame) {
+  support_->SetNeedsBeginFrame(needs_begin_frame);
+}
+
+void CompositorFrameSinkImpl::SubmitCompositorFrame(
+    const LocalSurfaceId& local_surface_id,
+    cc::CompositorFrame frame,
+    mojom::HitTestRegionListPtr hit_test_region_list) {
+  // TODO(gklassen): Route hit-test data to the appropriate HitTestAggregator.
+  if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
+    compositor_frame_sink_binding_.CloseWithReason(
+        1, "Surface invariants violation");
+    OnClientConnectionLost();
+  }
+}
+
+void CompositorFrameSinkImpl::DidNotProduceFrame(
+    const BeginFrameAck& begin_frame_ack) {
+  support_->DidNotProduceFrame(begin_frame_ack);
+}
+
+void CompositorFrameSinkImpl::DidReceiveCompositorFrameAck(
+    const std::vector<ReturnedResource>& resources) {
+  if (client_)
+    client_->DidReceiveCompositorFrameAck(resources);
+}
+
+void CompositorFrameSinkImpl::OnBeginFrame(const BeginFrameArgs& args) {
+  if (client_)
+    client_->OnBeginFrame(args);
+}
+
+void CompositorFrameSinkImpl::OnBeginFramePausedChanged(bool paused) {
+  if (client_)
+    client_->OnBeginFramePausedChanged(paused);
+}
+
+void CompositorFrameSinkImpl::ReclaimResources(
+    const std::vector<ReturnedResource>& resources) {
+  if (client_)
+    client_->ReclaimResources(resources);
+}
+
+void CompositorFrameSinkImpl::WillDrawSurface(
+    const LocalSurfaceId& local_surface_id,
+    const gfx::Rect& damage_rect) {}
+
+void CompositorFrameSinkImpl::OnClientConnectionLost() {
+  support_->frame_sink_manager()->OnClientConnectionLost(
+      support_->frame_sink_id());
+}
+
+}  // namespace viz
diff --git a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h b/components/viz/service/frame_sinks/compositor_frame_sink_impl.h
similarity index 75%
rename from components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
rename to components/viz/service/frame_sinks/compositor_frame_sink_impl.h
index 7150e34c..49793396 100644
--- a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
-#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_IMPL_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_IMPL_H_
 
 #include <memory>
 #include <vector>
@@ -20,16 +20,16 @@
 namespace viz {
 
 // Server side representation of a WindowSurface.
-class GpuCompositorFrameSink
+class CompositorFrameSinkImpl
     : public NON_EXPORTED_BASE(CompositorFrameSinkSupportClient),
       public NON_EXPORTED_BASE(mojom::CompositorFrameSink) {
  public:
-  GpuCompositorFrameSink(FrameSinkManagerImpl* frame_sink_manager,
-                         const FrameSinkId& frame_sink_id,
-                         mojom::CompositorFrameSinkRequest request,
-                         mojom::CompositorFrameSinkClientPtr client);
+  CompositorFrameSinkImpl(FrameSinkManagerImpl* frame_sink_manager,
+                          const FrameSinkId& frame_sink_id,
+                          mojom::CompositorFrameSinkRequest request,
+                          mojom::CompositorFrameSinkClientPtr client);
 
-  ~GpuCompositorFrameSink() override;
+  ~CompositorFrameSinkImpl() override;
 
   // mojom::CompositorFrameSink:
   void SetNeedsBeginFrame(bool needs_begin_frame) override;
@@ -57,9 +57,9 @@
   mojom::CompositorFrameSinkClientPtr client_;
   mojo::Binding<mojom::CompositorFrameSink> compositor_frame_sink_binding_;
 
-  DISALLOW_COPY_AND_ASSIGN(GpuCompositorFrameSink);
+  DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkImpl);
 };
 
 }  // namespace viz
 
-#endif  // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
+#endif  // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_IMPL_H_
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 0e07c38..749d7913 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -10,10 +10,10 @@
 #include "base/logging.h"
 #include "components/viz/service/display/display.h"
 #include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_impl.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_client.h"
-#include "components/viz/service/frame_sinks/gpu_compositor_frame_sink.h"
-#include "components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h"
 #include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
+#include "components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h"
 
 #if DCHECK_IS_ON()
 #include <sstream>
@@ -98,7 +98,7 @@
       frame_sink_id, surface_handle, renderer_settings, &begin_frame_source);
 
   compositor_frame_sinks_[frame_sink_id] =
-      base::MakeUnique<GpuRootCompositorFrameSink>(
+      base::MakeUnique<RootCompositorFrameSinkImpl>(
           this, frame_sink_id, std::move(display),
           std::move(begin_frame_source), std::move(request), std::move(client),
           std::move(display_private_request));
@@ -112,7 +112,7 @@
   DCHECK_EQ(0u, compositor_frame_sinks_.count(frame_sink_id));
 
   compositor_frame_sinks_[frame_sink_id] =
-      base::MakeUnique<GpuCompositorFrameSink>(
+      base::MakeUnique<CompositorFrameSinkImpl>(
           this, frame_sink_id, std::move(request), std::move(client));
 }
 
diff --git a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc b/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
deleted file mode 100644
index 3485356..0000000
--- a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/service/frame_sinks/gpu_compositor_frame_sink.h"
-
-#include <utility>
-
-#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
-
-namespace viz {
-
-GpuCompositorFrameSink::GpuCompositorFrameSink(
-    FrameSinkManagerImpl* frame_sink_manager,
-    const FrameSinkId& frame_sink_id,
-    mojom::CompositorFrameSinkRequest request,
-    mojom::CompositorFrameSinkClientPtr client)
-    : support_(CompositorFrameSinkSupport::Create(
-          this,
-          frame_sink_manager,
-          frame_sink_id,
-          false /* is_root */,
-          true /* needs_sync_points */)),
-      client_(std::move(client)),
-      compositor_frame_sink_binding_(this, std::move(request)) {
-  compositor_frame_sink_binding_.set_connection_error_handler(base::Bind(
-      &GpuCompositorFrameSink::OnClientConnectionLost, base::Unretained(this)));
-}
-
-GpuCompositorFrameSink::~GpuCompositorFrameSink() = default;
-
-void GpuCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
-  support_->SetNeedsBeginFrame(needs_begin_frame);
-}
-
-void GpuCompositorFrameSink::SubmitCompositorFrame(
-    const LocalSurfaceId& local_surface_id,
-    cc::CompositorFrame frame,
-    mojom::HitTestRegionListPtr hit_test_region_list) {
-  // TODO(gklassen): Route hit-test data to the appropriate HitTestAggregator.
-  if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
-    compositor_frame_sink_binding_.CloseWithReason(
-        1, "Surface invariants violation");
-    OnClientConnectionLost();
-  }
-}
-
-void GpuCompositorFrameSink::DidNotProduceFrame(
-    const BeginFrameAck& begin_frame_ack) {
-  support_->DidNotProduceFrame(begin_frame_ack);
-}
-
-void GpuCompositorFrameSink::DidReceiveCompositorFrameAck(
-    const std::vector<ReturnedResource>& resources) {
-  if (client_)
-    client_->DidReceiveCompositorFrameAck(resources);
-}
-
-void GpuCompositorFrameSink::OnBeginFrame(const BeginFrameArgs& args) {
-  if (client_)
-    client_->OnBeginFrame(args);
-}
-
-void GpuCompositorFrameSink::OnBeginFramePausedChanged(bool paused) {
-  if (client_)
-    client_->OnBeginFramePausedChanged(paused);
-}
-
-void GpuCompositorFrameSink::ReclaimResources(
-    const std::vector<ReturnedResource>& resources) {
-  if (client_)
-    client_->ReclaimResources(resources);
-}
-
-void GpuCompositorFrameSink::WillDrawSurface(
-    const LocalSurfaceId& local_surface_id,
-    const gfx::Rect& damage_rect) {}
-
-void GpuCompositorFrameSink::OnClientConnectionLost() {
-  support_->frame_sink_manager()->OnClientConnectionLost(
-      support_->frame_sink_id());
-}
-
-}  // namespace viz
diff --git a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
similarity index 74%
rename from components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
rename to components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index 18759fc5..7065fba5 100644
--- a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h"
+#include "components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h"
 
 #include <utility>
 
@@ -13,7 +13,7 @@
 
 namespace viz {
 
-GpuRootCompositorFrameSink::GpuRootCompositorFrameSink(
+RootCompositorFrameSinkImpl::RootCompositorFrameSinkImpl(
     FrameSinkManagerImpl* frame_sink_manager,
     const FrameSinkId& frame_sink_id,
     std::unique_ptr<Display> display,
@@ -35,50 +35,50 @@
       hit_test_aggregator_(this) {
   DCHECK(display_begin_frame_source_);
   compositor_frame_sink_binding_.set_connection_error_handler(
-      base::Bind(&GpuRootCompositorFrameSink::OnClientConnectionLost,
+      base::Bind(&RootCompositorFrameSinkImpl::OnClientConnectionLost,
                  base::Unretained(this)));
   frame_sink_manager->RegisterBeginFrameSource(
       display_begin_frame_source_.get(), frame_sink_id);
   display_->Initialize(this, frame_sink_manager->surface_manager());
 }
 
-GpuRootCompositorFrameSink::~GpuRootCompositorFrameSink() {
+RootCompositorFrameSinkImpl::~RootCompositorFrameSinkImpl() {
   support_->frame_sink_manager()->UnregisterBeginFrameSource(
       display_begin_frame_source_.get());
 }
 
-void GpuRootCompositorFrameSink::SetDisplayVisible(bool visible) {
+void RootCompositorFrameSinkImpl::SetDisplayVisible(bool visible) {
   DCHECK(display_);
   display_->SetVisible(visible);
 }
 
-void GpuRootCompositorFrameSink::ResizeDisplay(const gfx::Size& size) {
+void RootCompositorFrameSinkImpl::ResizeDisplay(const gfx::Size& size) {
   DCHECK(display_);
   display_->Resize(size);
 }
 
-void GpuRootCompositorFrameSink::SetDisplayColorSpace(
+void RootCompositorFrameSinkImpl::SetDisplayColorSpace(
     const gfx::ColorSpace& color_space) {
   DCHECK(display_);
   display_->SetColorSpace(color_space, color_space);
 }
 
-void GpuRootCompositorFrameSink::SetOutputIsSecure(bool secure) {
+void RootCompositorFrameSinkImpl::SetOutputIsSecure(bool secure) {
   DCHECK(display_);
   display_->SetOutputIsSecure(secure);
 }
 
-void GpuRootCompositorFrameSink::SetLocalSurfaceId(
+void RootCompositorFrameSinkImpl::SetLocalSurfaceId(
     const LocalSurfaceId& local_surface_id,
     float scale_factor) {
   display_->SetLocalSurfaceId(local_surface_id, scale_factor);
 }
 
-void GpuRootCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
+void RootCompositorFrameSinkImpl::SetNeedsBeginFrame(bool needs_begin_frame) {
   support_->SetNeedsBeginFrame(needs_begin_frame);
 }
 
-void GpuRootCompositorFrameSink::SubmitCompositorFrame(
+void RootCompositorFrameSinkImpl::SubmitCompositorFrame(
     const LocalSurfaceId& local_surface_id,
     cc::CompositorFrame frame,
     mojom::HitTestRegionListPtr hit_test_region_list) {
@@ -91,12 +91,12 @@
   }
 }
 
-void GpuRootCompositorFrameSink::DidNotProduceFrame(
+void RootCompositorFrameSinkImpl::DidNotProduceFrame(
     const BeginFrameAck& begin_frame_ack) {
   support_->DidNotProduceFrame(begin_frame_ack);
 }
 
-void GpuRootCompositorFrameSink::OnAggregatedHitTestRegionListUpdated(
+void RootCompositorFrameSinkImpl::OnAggregatedHitTestRegionListUpdated(
     mojo::ScopedSharedBufferHandle active_handle,
     uint32_t active_handle_size,
     mojo::ScopedSharedBufferHandle idle_handle,
@@ -106,53 +106,53 @@
       std::move(idle_handle), idle_handle_size);
 }
 
-void GpuRootCompositorFrameSink::SwitchActiveAggregatedHitTestRegionList(
+void RootCompositorFrameSinkImpl::SwitchActiveAggregatedHitTestRegionList(
     uint8_t active_handle_index) {
   support_->frame_sink_manager()->SwitchActiveAggregatedHitTestRegionList(
       support_->frame_sink_id(), active_handle_index);
 }
 
-void GpuRootCompositorFrameSink::DisplayOutputSurfaceLost() {
+void RootCompositorFrameSinkImpl::DisplayOutputSurfaceLost() {
   // TODO(staraz): Implement this. Client should hear about context/output
   // surface lost.
 }
 
-void GpuRootCompositorFrameSink::DisplayWillDrawAndSwap(
+void RootCompositorFrameSinkImpl::DisplayWillDrawAndSwap(
     bool will_draw_and_swap,
     const cc::RenderPassList& render_pass) {
   hit_test_aggregator_.PostTaskAggregate(display_->CurrentSurfaceId());
 }
 
-void GpuRootCompositorFrameSink::DisplayDidDrawAndSwap() {}
+void RootCompositorFrameSinkImpl::DisplayDidDrawAndSwap() {}
 
-void GpuRootCompositorFrameSink::DidReceiveCompositorFrameAck(
+void RootCompositorFrameSinkImpl::DidReceiveCompositorFrameAck(
     const std::vector<ReturnedResource>& resources) {
   if (client_)
     client_->DidReceiveCompositorFrameAck(resources);
 }
 
-void GpuRootCompositorFrameSink::OnBeginFrame(const BeginFrameArgs& args) {
+void RootCompositorFrameSinkImpl::OnBeginFrame(const BeginFrameArgs& args) {
   hit_test_aggregator_.Swap();
   if (client_)
     client_->OnBeginFrame(args);
 }
 
-void GpuRootCompositorFrameSink::OnBeginFramePausedChanged(bool paused) {
+void RootCompositorFrameSinkImpl::OnBeginFramePausedChanged(bool paused) {
   if (client_)
     client_->OnBeginFramePausedChanged(paused);
 }
 
-void GpuRootCompositorFrameSink::ReclaimResources(
+void RootCompositorFrameSinkImpl::ReclaimResources(
     const std::vector<ReturnedResource>& resources) {
   if (client_)
     client_->ReclaimResources(resources);
 }
 
-void GpuRootCompositorFrameSink::WillDrawSurface(
+void RootCompositorFrameSinkImpl::WillDrawSurface(
     const LocalSurfaceId& local_surface_id,
     const gfx::Rect& damage_rect) {}
 
-void GpuRootCompositorFrameSink::OnClientConnectionLost() {
+void RootCompositorFrameSinkImpl::OnClientConnectionLost() {
   support_->frame_sink_manager()->OnClientConnectionLost(
       support_->frame_sink_id());
 }
diff --git a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
similarity index 88%
rename from components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
rename to components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 375d119..38eaeee 100644
--- a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
-#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_ROOT_COMPOSITOR_FRAME_SINK_IMPL_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_ROOT_COMPOSITOR_FRAME_SINK_IMPL_H_
 
 #include <memory>
 
@@ -25,14 +25,14 @@
 class Display;
 class FrameSinkManagerImpl;
 
-class GpuRootCompositorFrameSink
+class RootCompositorFrameSinkImpl
     : public NON_EXPORTED_BASE(CompositorFrameSinkSupportClient),
       public NON_EXPORTED_BASE(mojom::CompositorFrameSink),
       public NON_EXPORTED_BASE(mojom::DisplayPrivate),
       public NON_EXPORTED_BASE(DisplayClient),
       public HitTestAggregatorDelegate {
  public:
-  GpuRootCompositorFrameSink(
+  RootCompositorFrameSinkImpl(
       FrameSinkManagerImpl* frame_sink_manager,
       const FrameSinkId& frame_sink_id,
       std::unique_ptr<Display> display,
@@ -41,7 +41,7 @@
       mojom::CompositorFrameSinkClientPtr client,
       mojom::DisplayPrivateAssociatedRequest display_private_request);
 
-  ~GpuRootCompositorFrameSink() override;
+  ~RootCompositorFrameSinkImpl() override;
 
   // mojom::DisplayPrivate:
   void SetDisplayVisible(bool visible) override;
@@ -89,7 +89,7 @@
 
   std::unique_ptr<CompositorFrameSinkSupport> support_;
 
-  // GpuRootCompositorFrameSink holds a Display and its BeginFrameSource if
+  // RootCompositorFrameSinkImpl holds a Display and its BeginFrameSource if
   // it was created with a non-null gpu::SurfaceHandle.
   std::unique_ptr<BeginFrameSource> display_begin_frame_source_;
   std::unique_ptr<Display> display_;
@@ -101,9 +101,9 @@
 
   HitTestAggregator hit_test_aggregator_;
 
-  DISALLOW_COPY_AND_ASSIGN(GpuRootCompositorFrameSink);
+  DISALLOW_COPY_AND_ASSIGN(RootCompositorFrameSinkImpl);
 };
 
 }  // namespace viz
 
-#endif  // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
+#endif  // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_ROOT_COMPOSITOR_FRAME_SINK_IMPL_H_
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
index c079515..6c60508 100644
--- a/content/browser/appcache/appcache_backend_impl.cc
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -99,13 +99,14 @@
   return host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
 }
 
-bool AppCacheBackendImpl::GetStatusWithCallback(
-    int host_id, const GetStatusCallback& callback, void* callback_param) {
+bool AppCacheBackendImpl::GetStatusWithCallback(int host_id,
+                                                GetStatusCallback callback,
+                                                void* callback_param) {
   AppCacheHost* host = GetHost(host_id);
   if (!host)
     return false;
 
-  host->GetStatusWithCallback(callback, callback_param);
+  host->GetStatusWithCallback(std::move(callback), callback_param);
   return true;
 }
 
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index 83e64632..6c6b90b 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -44,7 +44,8 @@
   bool MarkAsForeignEntry(int host_id,
                           const GURL& document_url,
                           int64_t cache_document_was_loaded_from);
-  bool GetStatusWithCallback(int host_id, const GetStatusCallback& callback,
+  bool GetStatusWithCallback(int host_id,
+                             GetStatusCallback callback,
                              void* callback_param);
   bool StartUpdateWithCallback(int host_id, const StartUpdateCallback& callback,
                                void* callback_param);
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc
index 37fee079..2d18a23 100644
--- a/content/browser/appcache/appcache_dispatcher_host.cc
+++ b/content/browser/appcache/appcache_dispatcher_host.cc
@@ -31,8 +31,6 @@
 
   backend_impl_.Initialize(appcache_service_.get(), &frontend_proxy_,
                            process_id_);
-  get_status_callback_ = base::Bind(&AppCacheDispatcherHost::GetStatusCallback,
-                                    weak_factory_.GetWeakPtr());
   start_update_callback_ = base::Bind(
       &AppCacheDispatcherHost::StartUpdateCallback, weak_factory_.GetWeakPtr());
   swap_cache_callback_ = base::Bind(&AppCacheDispatcherHost::SwapCacheCallback,
@@ -169,8 +167,11 @@
 
   pending_reply_msg_.reset(reply_msg);
   if (appcache_service_.get()) {
-    if (!backend_impl_.GetStatusWithCallback(host_id, get_status_callback_,
-                                             reply_msg)) {
+    if (!backend_impl_.GetStatusWithCallback(
+            host_id,
+            base::BindOnce(&AppCacheDispatcherHost::GetStatusCallback,
+                           weak_factory_.GetWeakPtr()),
+            reply_msg)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_GET_STATUS);
     }
     return;
diff --git a/content/browser/appcache/appcache_dispatcher_host.h b/content/browser/appcache/appcache_dispatcher_host.h
index 0b4b16e..be503446 100644
--- a/content/browser/appcache/appcache_dispatcher_host.h
+++ b/content/browser/appcache/appcache_dispatcher_host.h
@@ -66,7 +66,6 @@
   AppCacheFrontendProxy frontend_proxy_;
   AppCacheBackendImpl backend_impl_;
 
-  content::GetStatusCallback get_status_callback_;
   content::StartUpdateCallback start_update_callback_;
   content::SwapCacheCallback swap_cache_callback_;
   std::unique_ptr<IPC::Message> pending_reply_msg_;
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index ecfa39c..609121f 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -210,13 +210,13 @@
   return true;
 }
 
-void AppCacheHost::GetStatusWithCallback(const GetStatusCallback& callback,
+void AppCacheHost::GetStatusWithCallback(GetStatusCallback callback,
                                          void* callback_param) {
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
          pending_get_status_callback_.is_null());
 
-  pending_get_status_callback_ = callback;
+  pending_get_status_callback_ = std::move(callback);
   pending_callback_param_ = callback_param;
   if (is_selection_pending())
     return;
@@ -227,8 +227,8 @@
 void AppCacheHost::DoPendingGetStatus() {
   DCHECK_EQ(false, pending_get_status_callback_.is_null());
 
-  pending_get_status_callback_.Run(GetStatus(), pending_callback_param_);
-  pending_get_status_callback_.Reset();
+  std::move(pending_get_status_callback_)
+      .Run(GetStatus(), pending_callback_param_);
   pending_callback_param_ = NULL;
 }
 
@@ -259,8 +259,8 @@
     }
   }
 
-  pending_start_update_callback_.Run(success, pending_callback_param_);
-  pending_start_update_callback_.Reset();
+  std::move(pending_start_update_callback_)
+      .Run(success, pending_callback_param_);
   pending_callback_param_ = NULL;
 }
 
@@ -295,8 +295,7 @@
     }
   }
 
-  pending_swap_cache_callback_.Run(success, pending_callback_param_);
-  pending_swap_cache_callback_.Reset();
+  std::move(pending_swap_cache_callback_).Run(success, pending_callback_param_);
   pending_callback_param_ = NULL;
 }
 
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index 2cbf435..c237e5f 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -52,7 +52,7 @@
 class AppCacheTest;
 class AppCacheUpdateJobTest;
 
-typedef base::Callback<void(AppCacheStatus, void*)> GetStatusCallback;
+typedef base::OnceCallback<void(AppCacheStatus, void*)> GetStatusCallback;
 typedef base::Callback<void(bool, void*)> StartUpdateCallback;
 typedef base::Callback<void(bool, void*)> SwapCacheCallback;
 
@@ -92,8 +92,7 @@
   bool SelectCacheForSharedWorker(int64_t appcache_id);
   bool MarkAsForeignEntry(const GURL& document_url,
                           int64_t cache_document_was_loaded_from);
-  void GetStatusWithCallback(const GetStatusCallback& callback,
-                             void* callback_param);
+  void GetStatusWithCallback(GetStatusCallback callback, void* callback_param);
   void StartUpdateWithCallback(const StartUpdateCallback& callback,
                                void* callback_param);
   void SwapCacheWithCallback(const SwapCacheCallback& callback,
diff --git a/content/browser/appcache/appcache_host_unittest.cc b/content/browser/appcache/appcache_host_unittest.cc
index 16c3626..eac9155 100644
--- a/content/browser/appcache/appcache_host_unittest.cc
+++ b/content/browser/appcache/appcache_host_unittest.cc
@@ -25,9 +25,8 @@
 class AppCacheHostTest : public testing::Test {
  public:
   AppCacheHostTest() {
-    get_status_callback_ =
-        base::Bind(&AppCacheHostTest::GetStatusCallback,
-                   base::Unretained(this));
+    get_status_callback_ = base::BindRepeating(
+        &AppCacheHostTest::GetStatusCallback, base::Unretained(this));
     start_update_callback_ =
         base::Bind(&AppCacheHostTest::StartUpdateCallback,
                    base::Unretained(this));
@@ -177,7 +176,8 @@
   // See that the callbacks are delivered immediately
   // and respond as if there is no cache selected.
   last_status_result_ = APPCACHE_STATUS_OBSOLETE;
-  host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+  host.GetStatusWithCallback(std::move(get_status_callback_),
+                             reinterpret_cast<void*>(1));
   EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
   EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
 
@@ -301,7 +301,8 @@
   // The callback should not occur until we finish cache selection.
   last_status_result_ = APPCACHE_STATUS_OBSOLETE;
   last_callback_param_ = reinterpret_cast<void*>(-1);
-  host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+  host.GetStatusWithCallback(std::move(get_status_callback_),
+                             reinterpret_cast<void*>(1));
   EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
   EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
 
@@ -332,7 +333,8 @@
   // The callback should not occur until we finish cache selection.
   last_status_result_ = APPCACHE_STATUS_OBSOLETE;
   last_callback_param_ = reinterpret_cast<void*>(-1);
-  host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+  host.GetStatusWithCallback(std::move(get_status_callback_),
+                             reinterpret_cast<void*>(1));
   EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
   EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
 
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 1a1d723..dee8f01 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -29,6 +29,7 @@
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
 #include "components/viz/common/gl_helper.h"
 #include "components/viz/host/host_frame_sink_manager.h"
+#include "components/viz/host/renderer_settings_creation.h"
 #include "components/viz/service/display/display.h"
 #include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
@@ -60,7 +61,6 @@
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_constants.h"
 #include "ui/compositor/compositor_switches.h"
-#include "ui/compositor/compositor_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display_switches.h"
 #include "ui/display/types/display_snapshot.h"
@@ -235,7 +235,7 @@
     scoped_refptr<base::SingleThreadTaskRunner> resize_task_runner)
     : frame_sink_id_allocator_(kDefaultClientId),
       renderer_settings_(
-          ui::CreateRendererSettings(CreateBufferToTextureTargetMap())),
+          viz::CreateRendererSettings(CreateBufferToTextureTargetMap())),
       resize_task_runner_(std::move(resize_task_runner)),
       task_graph_runner_(new cc::SingleThreadTaskGraphRunner),
       callback_factory_(this) {
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index 99fd473..a9574ec 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/public/common/url_utils.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_utils.h"
 #include "content/test/test_navigation_url_loader.h"
 #include "content/test/test_render_frame_host.h"
@@ -1009,9 +1010,10 @@
 
   // Do a renderer-initiated navigation to a data url. The request should be
   // sent to the IO thread.
-  TestRenderFrameHost* main_rfh = main_test_rfh();
-  main_rfh->SendRendererInitiatedNavigationRequest(kUrl2, true);
-  EXPECT_TRUE(main_rfh->is_loading());
+  auto navigation_to_data_url =
+      NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh());
+  navigation_to_data_url->Start();
+  EXPECT_TRUE(main_test_rfh()->is_loading());
   EXPECT_TRUE(node->navigation_request());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 }
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 1434b848..19d0a4e3 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -46,6 +46,7 @@
 #include "content/public/common/url_utils.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_notification_tracker.h"
 #include "content/public/test/test_utils.h"
 #include "content/test/test_content_browser_client.h"
@@ -3124,7 +3125,9 @@
 
   // The initial RFH receives a BeginNavigation IPC. The navigation should not
   // start.
-  initial_rfh->SendRendererInitiatedNavigationRequest(kUrl3, true);
+  auto navigation =
+      NavigationSimulator::CreateRendererInitiated(kUrl3, initial_rfh);
+  navigation->Start();
   EXPECT_FALSE(main_test_rfh()->frame_tree_node()->navigation_request());
 }
 
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index 9a8a794..b1f39e7 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -816,10 +816,22 @@
     }
   }
 
-  // TODO(mcnee): Allow the root RWHV to consume the child's
-  // GestureScrollUpdates. This is needed to prevent the child from consuming
-  // them after the root has started an overscroll.
-  // See crbug.com/713368
+  // Allow the root RWHV a chance to consume the child's GestureScrollUpdates
+  // in case the root needs to prevent the child from scrolling. For example,
+  // if the root has started an overscroll gesture, it needs to process the
+  // scroll events that would normally be processed by the child.
+  // TODO(mcnee): Once BrowserPlugin is removed, investigate routing these
+  // GestureScrollUpdates directly to the root RWHV during an overscroll
+  // gesture. The way resending of scroll events from a plugin works would cause
+  // issues with this approach in terms of valid input streams.
+  // See crbug.com/751782
+  if (frame_connector_ &&
+      input_event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
+    const blink::WebGestureEvent& gesture_event =
+        static_cast<const blink::WebGestureEvent&>(input_event);
+    return frame_connector_->GetRootRenderWidgetHostView()
+        ->FilterChildGestureEvent(gesture_event);
+  }
 
   return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
 }
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index c95d63b4..7b3faf6 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -65,6 +65,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/runner/common/client_util.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
 #include "ui/gfx/color_space_switches.h"
 #include "ui/gfx/switches.h"
@@ -98,10 +99,6 @@
 #include "gpu/ipc/common/gpu_surface_tracker.h"
 #endif
 
-#if defined(OS_MACOSX)
-#include "ui/base/ui_base_switches.h"
-#endif
-
 namespace content {
 
 bool GpuProcessHost::gpu_enabled_ = true;
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a1dbca3b..1adfb73 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2594,7 +2594,6 @@
     // also be added to chrome/browser/chromeos/login/chrome_restart_request.cc.
     cc::switches::kDisableCompositedAntialiasing,
     cc::switches::kDisableThreadedAnimation,
-    cc::switches::kDisallowNonExactResourceReuse,
     cc::switches::kEnableGpuBenchmarking,
     cc::switches::kEnableLayerLists,
     cc::switches::kEnableTileCompression,
@@ -2625,6 +2624,7 @@
 #endif
     switches::kEnableLowEndDeviceMode,
     switches::kDisableLowEndDeviceMode,
+    switches::kDisallowNonExactResourceReuse,
 #if defined(OS_ANDROID)
     switches::kDisableMediaSessionAPI,
     switches::kRendererWaitForJavaDebugger,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 48918fe..42ae340 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -85,6 +85,7 @@
 #include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/event.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 9c99a34..089869f5 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1798,6 +1798,14 @@
   return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
 }
 
+InputEventAckState RenderWidgetHostViewAndroid::FilterChildGestureEvent(
+    const blink::WebGestureEvent& gesture_event) {
+  if (overscroll_controller_ &&
+      overscroll_controller_->WillHandleGestureEvent(gesture_event))
+    return INPUT_EVENT_ACK_STATE_CONSUMED;
+  return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+}
+
 BrowserAccessibilityManager*
     RenderWidgetHostViewAndroid::CreateBrowserAccessibilityManager(
         BrowserAccessibilityDelegate* delegate, bool for_root_frame) {
@@ -2417,4 +2425,11 @@
       overscroll_refresh_handler, compositor, view_.GetDipScale());
 }
 
+void RenderWidgetHostViewAndroid::SetOverscrollControllerForTesting(
+    ui::OverscrollRefreshHandler* overscroll_refresh_handler) {
+  overscroll_controller_ = base::MakeUnique<OverscrollControllerAndroid>(
+      overscroll_refresh_handler, view_.GetWindowAndroid()->GetCompositor(),
+      view_.GetDipScale());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 57e85ed..cd82c4e 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -44,6 +44,7 @@
 
 namespace ui {
 class MotionEventAndroid;
+class OverscrollRefreshHandler;
 struct DidOverscrollParams;
 }
 
@@ -143,6 +144,8 @@
                               InputEventAckState ack_result) override;
   InputEventAckState FilterInputEvent(
       const blink::WebInputEvent& input_event) override;
+  InputEventAckState FilterChildGestureEvent(
+      const blink::WebGestureEvent& gesture_event) override;
   void GestureEventAck(const blink::WebGestureEvent& event,
                        InputEventAckState ack_result) override;
   BrowserAccessibilityManager* CreateBrowserAccessibilityManager(
@@ -320,6 +323,9 @@
   void SetSelectionControllerClientForTesting(
       std::unique_ptr<ui::TouchSelectionControllerClient> client);
 
+  void SetOverscrollControllerForTesting(
+      ui::OverscrollRefreshHandler* overscroll_refresh_handler);
+
   void GotFocus();
   void LostFocus();
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index d6480b6..e40ac83 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -76,6 +76,7 @@
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/content_browser_test_utils_internal.h"
+#include "content/test/mock_overscroll_observer.h"
 #include "ipc/ipc.mojom.h"
 #include "ipc/ipc_security_test_util.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -101,7 +102,10 @@
 #include "ui/native_theme/native_theme_features.h"
 
 #if defined(USE_AURA)
+#include "content/browser/renderer_host/overscroll_controller.h"
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/public/browser/overscroll_configuration.h"
+#include "content/test/mock_overscroll_controller_delegate_aura.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -116,6 +120,7 @@
 #include "content/browser/android/ime_adapter_android.h"
 #include "content/browser/renderer_host/input/touch_selection_controller_client_manager_android.h"
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/test/mock_overscroll_refresh_handler_android.h"
 #include "ui/gfx/geometry/point_f.h"
 #endif
 
@@ -1412,6 +1417,209 @@
   DCHECK_EQ(filter->last_rect().y(), 0);
 }
 
+#if defined(USE_AURA) || defined(OS_ANDROID)
+
+// When unconsumed scrolls in a child bubble to the root and start an
+// overscroll gesture, the subsequent gesture scroll update events should be
+// consumed by the root. The child should not be able to scroll during the
+// overscroll gesture.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+                       RootConsumesScrollDuringOverscrollGesture) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
+      root->current_frame_host()->GetRenderWidgetHost()->GetView());
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child_node = root->child_at(0);
+
+#if defined(USE_AURA)
+  // The child must be horizontally scrollable.
+  GURL child_url(embedded_test_server()->GetURL("b.com", "/wide_page.html"));
+#elif defined(OS_ANDROID)
+  // The child must be vertically scrollable.
+  GURL child_url(embedded_test_server()->GetURL("b.com", "/tall_page.html"));
+#endif
+  NavigateFrameToURL(child_node, child_url);
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  RenderWidgetHostViewChildFrame* rwhv_child =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          child_node->current_frame_host()->GetRenderWidgetHost()->GetView());
+
+  WaitForChildFrameSurfaceReady(child_node->current_frame_host());
+
+  ASSERT_EQ(gfx::Vector2dF(), rwhv_root->GetLastScrollOffset());
+  ASSERT_EQ(gfx::Vector2dF(), rwhv_child->GetLastScrollOffset());
+
+  RenderWidgetHostInputEventRouter* router =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetInputEventRouter();
+
+  {
+    // Set up the RenderWidgetHostInputEventRouter to send the gesture stream
+    // to the child.
+    const gfx::Rect root_bounds = rwhv_root->GetViewBounds();
+    const gfx::Rect child_bounds = rwhv_child->GetViewBounds();
+    const float page_scale_factor = GetPageScaleFactor(shell());
+    const gfx::Point point_in_child(
+        gfx::ToCeiledInt((child_bounds.x() - root_bounds.x() + 10) *
+                         page_scale_factor),
+        gfx::ToCeiledInt((child_bounds.y() - root_bounds.y() + 10) *
+                         page_scale_factor));
+    gfx::Point dont_care;
+    ASSERT_EQ(rwhv_child->GetRenderWidgetHost(),
+              router->GetRenderWidgetHostAtPoint(rwhv_root, point_in_child,
+                                                 &dont_care));
+
+    blink::WebTouchEvent touch_event(
+        blink::WebInputEvent::kTouchStart, blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::kTimeStampForTesting);
+    touch_event.touches_length = 1;
+    touch_event.touches[0].state = blink::WebTouchPoint::kStatePressed;
+    touch_event.touches[0].SetPositionInWidget(point_in_child.x(),
+                                               point_in_child.y());
+    touch_event.unique_touch_event_id = 1;
+    router->RouteTouchEvent(rwhv_root, &touch_event,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+
+    blink::WebGestureEvent gesture_event(
+        blink::WebInputEvent::kGestureTapDown,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::kTimeStampForTesting);
+    gesture_event.source_device = blink::kWebGestureDeviceTouchscreen;
+    gesture_event.unique_touch_event_id = touch_event.unique_touch_event_id;
+    router->RouteGestureEvent(rwhv_root, &gesture_event,
+                              ui::LatencyInfo(ui::SourceEventType::TOUCH));
+  }
+
+#if defined(USE_AURA)
+  RenderWidgetHostViewAura* rwhva =
+      static_cast<RenderWidgetHostViewAura*>(rwhv_root);
+  std::unique_ptr<MockOverscrollControllerDelegateAura>
+      mock_overscroll_delegate =
+          base::MakeUnique<MockOverscrollControllerDelegateAura>(rwhva);
+  rwhva->overscroll_controller()->set_delegate(mock_overscroll_delegate.get());
+  MockOverscrollObserver* mock_overscroll_observer =
+      mock_overscroll_delegate.get();
+#elif defined(OS_ANDROID)
+  RenderWidgetHostViewAndroid* rwhv_android =
+      static_cast<RenderWidgetHostViewAndroid*>(rwhv_root);
+  std::unique_ptr<MockOverscrollRefreshHandlerAndroid> mock_overscroll_handler =
+      base::MakeUnique<MockOverscrollRefreshHandlerAndroid>();
+  rwhv_android->SetOverscrollControllerForTesting(
+      mock_overscroll_handler.get());
+  MockOverscrollObserver* mock_overscroll_observer =
+      mock_overscroll_handler.get();
+#endif  // defined(USE_AURA)
+
+  std::unique_ptr<InputEventAckWaiter> gesture_begin_observer_child =
+      base::MakeUnique<InputEventAckWaiter>(
+          blink::WebInputEvent::kGestureScrollBegin);
+  child_node->current_frame_host()
+      ->GetRenderWidgetHost()
+      ->AddInputEventObserver(gesture_begin_observer_child.get());
+  std::unique_ptr<InputEventAckWaiter> gesture_end_observer_child =
+      base::MakeUnique<InputEventAckWaiter>(
+          blink::WebInputEvent::kGestureScrollEnd);
+  child_node->current_frame_host()
+      ->GetRenderWidgetHost()
+      ->AddInputEventObserver(gesture_end_observer_child.get());
+
+  // First we need our scroll to initiate an overscroll gesture in the root
+  // via unconsumed scrolls in the child.
+  blink::WebGestureEvent gesture_scroll_begin(
+      blink::WebGestureEvent::kGestureScrollBegin,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::kTimeStampForTesting);
+  gesture_scroll_begin.source_device = blink::kWebGestureDeviceTouchscreen;
+  gesture_scroll_begin.unique_touch_event_id = 1;
+  gesture_scroll_begin.data.scroll_begin.delta_hint_units =
+      blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
+  gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
+  gesture_scroll_begin.data.scroll_begin.delta_y_hint = 0.f;
+  router->RouteGestureEvent(rwhv_root, &gesture_scroll_begin,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+
+  // Make sure the child is indeed receiving the gesture stream.
+  gesture_begin_observer_child->Wait();
+
+  blink::WebGestureEvent gesture_scroll_update(
+      blink::WebGestureEvent::kGestureScrollUpdate,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::kTimeStampForTesting);
+  gesture_scroll_update.source_device = blink::kWebGestureDeviceTouchscreen;
+  gesture_scroll_update.unique_touch_event_id = 1;
+  gesture_scroll_update.data.scroll_update.delta_units =
+      blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
+  gesture_scroll_update.data.scroll_update.delta_x = 0.f;
+  gesture_scroll_update.data.scroll_update.delta_y = 0.f;
+#if defined(USE_AURA)
+  const float threshold =
+      GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN);
+  // For aura, we scroll horizontally to activate an overscroll navigation.
+  float* delta = &gesture_scroll_update.data.scroll_update.delta_x;
+#elif defined(OS_ANDROID)
+  const float threshold = 0.f;
+  // For android, we scroll vertically to activate pull-to-refresh.
+  float* delta = &gesture_scroll_update.data.scroll_update.delta_y;
+#endif
+  *delta = threshold + 1;
+  mock_overscroll_observer->Reset();
+  // This will bring us into an overscroll gesture.
+  router->RouteGestureEvent(rwhv_root, &gesture_scroll_update,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+  // Note that in addition to verifying that we get the overscroll update, it
+  // is necessary to wait before sending the next event to prevent our multiple
+  // GestureScrollUpdates from being coalesced.
+  mock_overscroll_observer->WaitForUpdate();
+
+  // This scroll is in the same direction and so it will contribute to the
+  // overscroll.
+  *delta = 10.0f;
+  mock_overscroll_observer->Reset();
+  router->RouteGestureEvent(rwhv_root, &gesture_scroll_update,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+  mock_overscroll_observer->WaitForUpdate();
+
+  // Now we reverse direction. The child could scroll in this direction, but
+  // since we're in an overscroll gesture, the root should consume it.
+  *delta = -5.0f;
+  mock_overscroll_observer->Reset();
+  router->RouteGestureEvent(rwhv_root, &gesture_scroll_update,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+  mock_overscroll_observer->WaitForUpdate();
+
+  blink::WebGestureEvent gesture_scroll_end(
+      blink::WebGestureEvent::kGestureScrollEnd,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::kTimeStampForTesting);
+  gesture_scroll_end.source_device = blink::kWebGestureDeviceTouchscreen;
+  gesture_scroll_end.unique_touch_event_id = 1;
+  gesture_scroll_end.data.scroll_end.delta_units =
+      blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
+  mock_overscroll_observer->Reset();
+  router->RouteGestureEvent(rwhv_root, &gesture_scroll_end,
+                            ui::LatencyInfo(ui::SourceEventType::TOUCH));
+  mock_overscroll_observer->WaitForEnd();
+
+  // Ensure that the method of providing the child's scroll events to the root
+  // does not leave the child in an invalid state.
+  gesture_end_observer_child->Wait();
+}
+#endif  // defined(USE_AURA) || defined(OS_ANDROID)
+
 // Test that an ET_SCROLL event sent to an out-of-process iframe correctly
 // results in a scroll. This is only handled by RenderWidgetHostViewAura
 // and is needed for trackpad scrolling on Chromebooks.
diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc
index 03bc4cbb..ad60ac6 100644
--- a/content/browser/speech/speech_recognizer_impl.cc
+++ b/content/browser/speech/speech_recognizer_impl.cc
@@ -565,8 +565,8 @@
   DCHECK(recognition_engine_.get() != NULL);
   DCHECK(!IsCapturingAudio());
   GetAudioSystem()->GetInputStreamParameters(
-      device_id_, base::Bind(&SpeechRecognizerImpl::OnDeviceInfo,
-                             weak_ptr_factory_.GetWeakPtr()));
+      device_id_, base::BindOnce(&SpeechRecognizerImpl::OnDeviceInfo,
+                                 weak_ptr_factory_.GetWeakPtr()));
 
   listener()->OnRecognitionStart(session_id());
   return STATE_PREPARING;
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 8af8d348..aa77b714 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -46,6 +46,7 @@
 #include "content/public/common/content_constants.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_utils.h"
 #include "content/test/test_content_browser_client.h"
@@ -1041,10 +1042,7 @@
 
   // Simulate a link click in first contents to second site.  Doesn't switch
   // SiteInstances, because we don't intercept Blink navigations.
-  orig_rfh->SendRendererInitiatedNavigationRequest(url2, true);
-  orig_rfh->PrepareForCommit();
-  contents()->TestDidNavigate(orig_rfh, 0, true, url2,
-                              ui::PAGE_TRANSITION_TYPED);
+  NavigationSimulator::NavigateAndCommitFromDocument(url2, orig_rfh);
   SiteInstance* instance3 = contents()->GetSiteInstance();
   EXPECT_EQ(instance1, instance3);
   EXPECT_FALSE(contents()->CrossProcessNavigationPending());
@@ -3195,18 +3193,9 @@
 
   // Navigate the frame to another URL, which will send again
   // DidStartLoading and DidStopLoading messages.
-  {
-    subframe->SendRendererInitiatedNavigationRequest(foo_url, false);
-    subframe->PrepareForCommit();
-    if (!IsBrowserSideNavigationEnabled()) {
-      subframe->OnMessageReceived(
-          FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
-    }
-    subframe->SendNavigateWithTransition(10, false, foo_url,
-                                         ui::PAGE_TRANSITION_AUTO_SUBFRAME);
-    subframe->OnMessageReceived(
-        FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
-  }
+  NavigationSimulator::NavigateAndCommitFromDocument(foo_url, subframe);
+  subframe->OnMessageReceived(
+      FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
 
   // Since the main frame hasn't sent any DidStopLoading messages, it is
   // expected that the WebContents is still in loading state.
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 1647846..d094ec1f9 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -69,6 +69,7 @@
 #include "third_party/WebKit/public/web/WebKit.h"
 #include "third_party/WebKit/public/web/WebSelection.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/gfx/color_space_switches.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_switches.h"
@@ -550,7 +551,7 @@
       base::SharedMemory::GetHandleLimit() / 3;
 
   settings.disallow_non_exact_resource_reuse =
-      cmd.HasSwitch(cc::switches::kDisallowNonExactResourceReuse);
+      cmd.HasSwitch(switches::kDisallowNonExactResourceReuse);
 
   settings.wait_for_all_pipeline_stages_before_draw =
       cmd.HasSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw);
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index f733a47e..989b2a7 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -219,7 +219,7 @@
     // We want stable/baseline results when running layout tests.
     command_line.AppendSwitch(switches::kDisableSkiaRuntimeOpts);
 
-    command_line.AppendSwitch(cc::switches::kDisallowNonExactResourceReuse);
+    command_line.AppendSwitch(switches::kDisallowNonExactResourceReuse);
 
     // Unless/until WebM files are added to the media layout tests, we need to
     // avoid removing MP4/H264/AAC so that layout tests can run on Android.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4ced993..ddc1ae3 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -182,6 +182,7 @@
     "mock_keyboard_driver_win.h",
     "mock_leveldb_database.cc",
     "mock_leveldb_database.h",
+    "mock_overscroll_observer.h",
     "mock_permission_manager.cc",
     "mock_permission_manager.h",
     "mock_platform_notification_service.cc",
@@ -370,6 +371,11 @@
   }
 
   if (use_aura) {
+    sources += [
+      "mock_overscroll_controller_delegate_aura.cc",
+      "mock_overscroll_controller_delegate_aura.h",
+    ]
+
     deps += [
       "//ui/aura:test_support",
       "//ui/resources:ui_test_pak",
@@ -404,6 +410,11 @@
   }
 
   if (is_android) {
+    sources += [
+      "mock_overscroll_refresh_handler_android.cc",
+      "mock_overscroll_refresh_handler_android.h",
+    ]
+
     deps += [
       "//device/geolocation:geolocation_java",
       "//media/capture/video/android:android",
@@ -934,6 +945,7 @@
       "//content/shell/android:content_shell_assets",
       "//content/shell/android:content_shell_jni_headers",
       "//testing/android/native_test:native_test_support",
+      "//ui/android:android",
     ]
     android_manifest =
         "${target_gen_dir}/content_browsertests_manifest/AndroidManifest.xml"
diff --git a/content/test/data/wide_page.html b/content/test/data/wide_page.html
new file mode 100644
index 0000000..4f2ae7a
--- /dev/null
+++ b/content/test/data/wide_page.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <div>This page is wide.</div>
+  <div style="min-width: 1000px">Hello</div>
+</body>
+</html>
diff --git a/content/test/mock_overscroll_controller_delegate_aura.cc b/content/test/mock_overscroll_controller_delegate_aura.cc
new file mode 100644
index 0000000..e6f4c1e
--- /dev/null
+++ b/content/test/mock_overscroll_controller_delegate_aura.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/mock_overscroll_controller_delegate_aura.h"
+
+#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/public/test/test_utils.h"
+#include "ui/display/screen.h"
+
+namespace content {
+
+MockOverscrollControllerDelegateAura::MockOverscrollControllerDelegateAura(
+    RenderWidgetHostViewAura* rwhva)
+    : rwhva_(rwhva),
+      update_message_loop_runner_(new MessageLoopRunner),
+      end_message_loop_runner_(new MessageLoopRunner),
+      seen_update_(false),
+      overscroll_ended_(false) {}
+
+MockOverscrollControllerDelegateAura::~MockOverscrollControllerDelegateAura() {}
+
+gfx::Size MockOverscrollControllerDelegateAura::GetDisplaySize() const {
+  return display::Screen::GetScreen()
+      ->GetDisplayNearestView(rwhva_->GetNativeView())
+      .size();
+}
+
+base::Optional<float>
+MockOverscrollControllerDelegateAura::GetMaxOverscrollDelta() const {
+  return base::nullopt;
+}
+
+bool MockOverscrollControllerDelegateAura::OnOverscrollUpdate(float, float) {
+  seen_update_ = true;
+  if (update_message_loop_runner_->loop_running())
+    update_message_loop_runner_->Quit();
+  return true;
+}
+
+void MockOverscrollControllerDelegateAura::OnOverscrollComplete(
+    OverscrollMode) {
+  OnOverscrollEnd();
+}
+
+void MockOverscrollControllerDelegateAura::OnOverscrollModeChange(
+    OverscrollMode old_mode,
+    OverscrollMode new_mode,
+    OverscrollSource source) {
+  if (new_mode == OVERSCROLL_NONE)
+    OnOverscrollEnd();
+}
+
+void MockOverscrollControllerDelegateAura::WaitForUpdate() {
+  if (!seen_update_)
+    update_message_loop_runner_->Run();
+}
+
+void MockOverscrollControllerDelegateAura::WaitForEnd() {
+  if (!overscroll_ended_)
+    end_message_loop_runner_->Run();
+}
+
+void MockOverscrollControllerDelegateAura::Reset() {
+  update_message_loop_runner_ = new MessageLoopRunner;
+  end_message_loop_runner_ = new MessageLoopRunner;
+  seen_update_ = false;
+  overscroll_ended_ = false;
+}
+
+void MockOverscrollControllerDelegateAura::OnOverscrollEnd() {
+  overscroll_ended_ = true;
+  if (end_message_loop_runner_->loop_running())
+    end_message_loop_runner_->Quit();
+}
+
+}  // namespace content
diff --git a/content/test/mock_overscroll_controller_delegate_aura.h b/content/test/mock_overscroll_controller_delegate_aura.h
new file mode 100644
index 0000000..bcd2561
--- /dev/null
+++ b/content/test/mock_overscroll_controller_delegate_aura.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_MOCK_OVERSCROLL_CONTROLLER_DELEGATE_AURA_H_
+#define CONTENT_TEST_MOCK_OVERSCROLL_CONTROLLER_DELEGATE_AURA_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/renderer_host/overscroll_controller_delegate.h"
+#include "content/test/mock_overscroll_observer.h"
+
+namespace content {
+
+class MessageLoopRunner;
+class RenderWidgetHostViewAura;
+
+// Receives overscroll gesture updates from the aura overscroll controller.
+class MockOverscrollControllerDelegateAura
+    : public OverscrollControllerDelegate,
+      public MockOverscrollObserver {
+ public:
+  MockOverscrollControllerDelegateAura(RenderWidgetHostViewAura* rwhva);
+  ~MockOverscrollControllerDelegateAura() override;
+
+  // OverscrollControllerDelegate:
+  gfx::Size GetDisplaySize() const override;
+  base::Optional<float> GetMaxOverscrollDelta() const override;
+  bool OnOverscrollUpdate(float, float) override;
+  void OnOverscrollComplete(OverscrollMode) override;
+  void OnOverscrollModeChange(OverscrollMode old_mode,
+                              OverscrollMode new_mode,
+                              OverscrollSource source) override;
+
+  // MockOverscrollObserver:
+  void WaitForUpdate() override;
+  void WaitForEnd() override;
+  void Reset() override;
+
+ private:
+  void OnOverscrollEnd();
+
+  RenderWidgetHostViewAura* rwhva_;
+  scoped_refptr<MessageLoopRunner> update_message_loop_runner_;
+  scoped_refptr<MessageLoopRunner> end_message_loop_runner_;
+  bool seen_update_;
+  bool overscroll_ended_;
+  DISALLOW_COPY_AND_ASSIGN(MockOverscrollControllerDelegateAura);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_MOCK_OVERSCROLL_CONTROLLER_DELEGATE_AURA_H_
diff --git a/content/test/mock_overscroll_observer.h b/content/test/mock_overscroll_observer.h
new file mode 100644
index 0000000..4d3f5f5
--- /dev/null
+++ b/content/test/mock_overscroll_observer.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_MOCK_OVERSCROLL_OBSERVER_H_
+#define CONTENT_TEST_MOCK_OVERSCROLL_OBSERVER_H_
+
+namespace content {
+
+// An interface for tests to use MockOverscrollControllerDelegateAura and
+// MockOverscrollRefreshHandlerAndroid.
+class MockOverscrollObserver {
+ public:
+  MockOverscrollObserver() {}
+  virtual ~MockOverscrollObserver() {}
+  virtual void WaitForUpdate() = 0;
+  virtual void WaitForEnd() = 0;
+  virtual void Reset() = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_MOCK_OVERSCROLL_OBSERVER_H_
diff --git a/content/test/mock_overscroll_refresh_handler_android.cc b/content/test/mock_overscroll_refresh_handler_android.cc
new file mode 100644
index 0000000..7337058
--- /dev/null
+++ b/content/test/mock_overscroll_refresh_handler_android.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/mock_overscroll_refresh_handler_android.h"
+
+#include "content/public/test/test_utils.h"
+
+namespace content {
+
+MockOverscrollRefreshHandlerAndroid::MockOverscrollRefreshHandlerAndroid()
+    : ui::OverscrollRefreshHandler(nullptr) {}
+
+MockOverscrollRefreshHandlerAndroid::~MockOverscrollRefreshHandlerAndroid() {}
+
+bool MockOverscrollRefreshHandlerAndroid::PullStart() {
+  // The first GestureScrollUpdate starts the pull, but does not update the
+  // pull. For the purpose of testing, we'll be consistent with aura
+  // overscroll and consider this an update.
+  OnPullUpdate();
+  return true;
+}
+
+void MockOverscrollRefreshHandlerAndroid::PullUpdate(float) {
+  OnPullUpdate();
+}
+
+void MockOverscrollRefreshHandlerAndroid::PullRelease(bool) {
+  OnPullEnd();
+}
+
+void MockOverscrollRefreshHandlerAndroid::PullReset() {
+  OnPullEnd();
+}
+
+void MockOverscrollRefreshHandlerAndroid::WaitForUpdate() {
+  if (!seen_update_)
+    update_message_loop_runner_->Run();
+}
+
+void MockOverscrollRefreshHandlerAndroid::WaitForEnd() {
+  if (!pull_ended_)
+    end_message_loop_runner_->Run();
+}
+
+void MockOverscrollRefreshHandlerAndroid::Reset() {
+  update_message_loop_runner_ = new MessageLoopRunner;
+  end_message_loop_runner_ = new MessageLoopRunner;
+  seen_update_ = false;
+  pull_ended_ = false;
+}
+
+void MockOverscrollRefreshHandlerAndroid::OnPullUpdate() {
+  seen_update_ = true;
+  if (update_message_loop_runner_->loop_running())
+    update_message_loop_runner_->Quit();
+}
+
+void MockOverscrollRefreshHandlerAndroid::OnPullEnd() {
+  pull_ended_ = true;
+  if (end_message_loop_runner_->loop_running())
+    end_message_loop_runner_->Quit();
+}
+
+}  // namespace content
diff --git a/content/test/mock_overscroll_refresh_handler_android.h b/content/test/mock_overscroll_refresh_handler_android.h
new file mode 100644
index 0000000..e74cdb07
--- /dev/null
+++ b/content/test/mock_overscroll_refresh_handler_android.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_MOCK_OVERSCROLL_REFRESH_HANDLER_ANDROID_H_
+#define CONTENT_TEST_MOCK_OVERSCROLL_REFRESH_HANDLER_ANDROID_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/test/mock_overscroll_observer.h"
+#include "ui/android/overscroll_refresh_handler.h"
+
+namespace content {
+
+class MessageLoopRunner;
+
+// Receives overscroll gesture updates from the android overscroll controller.
+class MockOverscrollRefreshHandlerAndroid : public ui::OverscrollRefreshHandler,
+                                            public MockOverscrollObserver {
+ public:
+  MockOverscrollRefreshHandlerAndroid();
+  ~MockOverscrollRefreshHandlerAndroid() override;
+
+  // ui::OverscrollRefreshHandler:
+  bool PullStart() override;
+  void PullUpdate(float) override;
+  void PullRelease(bool) override;
+  void PullReset() override;
+
+  // MockOverscrollObserver:
+  void WaitForUpdate() override;
+  void WaitForEnd() override;
+  void Reset() override;
+
+ private:
+  void OnPullUpdate();
+  void OnPullEnd();
+
+  scoped_refptr<MessageLoopRunner> update_message_loop_runner_;
+  scoped_refptr<MessageLoopRunner> end_message_loop_runner_;
+  bool seen_update_;
+  bool pull_ended_;
+  DISALLOW_COPY_AND_ASSIGN(MockOverscrollRefreshHandlerAndroid);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_MOCK_OVERSCROLL_REFRESH_HANDLER_ANDROID_H_
diff --git a/extensions/renderer/bindings/api_event_handler_unittest.cc b/extensions/renderer/bindings/api_event_handler_unittest.cc
index 471f580..ac9dbb12 100644
--- a/extensions/renderer/bindings/api_event_handler_unittest.cc
+++ b/extensions/renderer/bindings/api_event_handler_unittest.cc
@@ -597,8 +597,9 @@
   EXPECT_EQ("[42]", GetStringPropertyFromObject(context->Global(), context,
                                                 "eventArgs"));
   ASSERT_EQ(1u, logged_errors.size());
-  EXPECT_EQ("Error in event handler: Uncaught Error: Event handler error",
-            logged_errors[0]);
+  EXPECT_THAT(logged_errors[0],
+              testing::StartsWith("Error in event handler: Error: "
+                                  "Event handler error"));
 }
 
 // Tests being notified as listeners are added or removed from events.
diff --git a/extensions/renderer/bindings/api_request_handler_unittest.cc b/extensions/renderer/bindings/api_request_handler_unittest.cc
index a5d365a8..cb7738e8 100644
--- a/extensions/renderer/bindings/api_request_handler_unittest.cc
+++ b/extensions/renderer/bindings/api_request_handler_unittest.cc
@@ -523,7 +523,8 @@
   // by the exception handler.
   EXPECT_FALSE(outer_try_catch.HasCaught());
   ASSERT_TRUE(logged_error);
-  EXPECT_EQ("Error handling response: Uncaught Error: hello", *logged_error);
+  EXPECT_THAT(*logged_error,
+              testing::StartsWith("Error handling response: Error: hello"));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/bindings/event_emitter_unittest.cc b/extensions/renderer/bindings/event_emitter_unittest.cc
index 5414e84..2ef4da0 100644
--- a/extensions/renderer/bindings/event_emitter_unittest.cc
+++ b/extensions/renderer/bindings/event_emitter_unittest.cc
@@ -12,6 +12,7 @@
 #include "extensions/renderer/bindings/api_event_listeners.h"
 #include "extensions/renderer/bindings/exception_handler.h"
 #include "gin/handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 namespace extensions {
 namespace {
@@ -126,7 +127,8 @@
             V8ToString(dispatch_result, context));
 
   ASSERT_EQ(1u, logged_errors.size());
-  EXPECT_EQ("Error in event handler: Uncaught Error: hahaha", logged_errors[0]);
+  EXPECT_THAT(logged_errors[0],
+              testing::StartsWith("Error in event handler: Error: hahaha"));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/bindings/exception_handler.cc b/extensions/renderer/bindings/exception_handler.cc
index 433fd706..899faa3 100644
--- a/extensions/renderer/bindings/exception_handler.cc
+++ b/extensions/renderer/bindings/exception_handler.cc
@@ -57,11 +57,22 @@
   v8::Isolate* isolate = context->GetIsolate();
   v8::HandleScope handle_scope(isolate);
 
-  v8::Local<v8::Message> v8_message = try_catch->Message();
+  v8::Local<v8::Value> message_value;
+  {
+    v8::TryCatch inner_try_catch(context->GetIsolate());
+    inner_try_catch.SetVerbose(true);
+    v8::Local<v8::Value> stack_trace_value;
+    if (try_catch->StackTrace(context).ToLocal(&stack_trace_value)) {
+      message_value = stack_trace_value;
+    } else if (!try_catch->Message().IsEmpty()) {
+      message_value = try_catch->Message()->Get();
+    }
+  }
+
   std::string full_message =
-      !v8_message.IsEmpty()
+      !message_value.IsEmpty()
           ? base::StringPrintf("%s: %s", message.c_str(),
-                               gin::V8ToString(v8_message->Get()).c_str())
+                               gin::V8ToString(message_value).c_str())
           : message;
   HandleException(context, full_message, try_catch->Exception());
   try_catch->Reset();  // Reset() to avoid handling the error more than once.
diff --git a/extensions/renderer/bindings/exception_handler_unittest.cc b/extensions/renderer/bindings/exception_handler_unittest.cc
index e79b351d..647620371 100644
--- a/extensions/renderer/bindings/exception_handler_unittest.cc
+++ b/extensions/renderer/bindings/exception_handler_unittest.cc
@@ -12,6 +12,7 @@
 #include "extensions/renderer/bindings/api_binding_test.h"
 #include "extensions/renderer/bindings/api_binding_test_util.h"
 #include "gin/converter.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 namespace extensions {
 
@@ -51,7 +52,7 @@
   ThrowException(context, "new Error('some error')", &handler);
 
   ASSERT_TRUE(logged_error);
-  EXPECT_EQ("handled: Uncaught Error: some error", *logged_error);
+  EXPECT_THAT(*logged_error, testing::StartsWith("handled: Error: some error"));
 }
 
 TEST_F(ExceptionHandlerTest, PerContextHandlers) {
@@ -73,9 +74,9 @@
   handler.SetHandlerForContext(context_a, custom_handler);
   ThrowException(context_a, "new Error('context a error')", &handler);
   EXPECT_FALSE(logged_error);
-  EXPECT_EQ("\"handled: Uncaught Error: context a error\"",
-            GetStringPropertyFromObject(context_a->Global(), context_a,
-                                        "loggedMessage"));
+  EXPECT_THAT(GetStringPropertyFromObject(context_a->Global(), context_a,
+                                          "loggedMessage"),
+              testing::StartsWith("\"handled: Error: context a error"));
   EXPECT_EQ("\"context a error\"",
             GetStringPropertyFromObject(context_a->Global(), context_a,
                                         "loggedExceptionMessage"));
@@ -94,7 +95,8 @@
 
   ThrowException(context_b, "new Error('context b error')", &handler);
   ASSERT_TRUE(logged_error);
-  EXPECT_EQ("handled: Uncaught Error: context b error", *logged_error);
+  EXPECT_THAT(*logged_error,
+              testing::StartsWith("handled: Error: context b error"));
   EXPECT_EQ("undefined", GetStringPropertyFromObject(
                              context_a->Global(), context_a, "loggedMessage"));
   EXPECT_EQ("undefined",
@@ -141,4 +143,67 @@
                                                      "loggedException"));
 }
 
+TEST_F(ExceptionHandlerTest, StackTraces) {
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = MainContext();
+
+  base::Optional<std::string> logged_error;
+  ExceptionHandler handler(base::Bind(&PopulateError, &logged_error),
+                           base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
+
+  {
+    v8::TryCatch try_catch(isolate());
+    v8::Local<v8::Script> script =
+        v8::Script::Compile(context,
+                            gin::StringToV8(context->GetIsolate(),
+                                            "throw new Error('simple');"))
+            .ToLocalChecked();
+    script->Run();
+    ASSERT_TRUE(try_catch.HasCaught());
+    handler.HandleException(context, "handled", &try_catch);
+
+    ASSERT_TRUE(logged_error);
+    EXPECT_EQ("handled: Error: simple\n    at <anonymous>:1:7", *logged_error);
+  }
+
+  logged_error.reset();
+
+  {
+    v8::TryCatch try_catch(isolate());
+    v8::Local<v8::Function> throw_error_function = FunctionFromString(
+        context, "(function() { throw new Error('function'); })");
+    ignore_result(throw_error_function->Call(context, v8::Undefined(isolate()),
+                                             0, nullptr));
+    ASSERT_TRUE(try_catch.HasCaught());
+    handler.HandleException(context, "handled", &try_catch);
+    ASSERT_TRUE(logged_error);
+    EXPECT_EQ("handled: Error: function\n    at <anonymous>:1:21",
+              *logged_error);
+  }
+
+  logged_error.reset();
+
+  {
+    v8::TryCatch try_catch(isolate());
+    const char kNestedCall[] =
+        "function throwError() { throw new Error('nested'); }\n"
+        "function callThrowError() { throwError(); }\n"
+        "callThrowError()\n";
+    v8::Local<v8::Script> script =
+        v8::Script::Compile(context,
+                            gin::StringToV8(context->GetIsolate(), kNestedCall))
+            .ToLocalChecked();
+    script->Run();
+    ASSERT_TRUE(try_catch.HasCaught());
+    handler.HandleException(context, "handled", &try_catch);
+    ASSERT_TRUE(logged_error);
+    const char kExpectedError[] =
+        "handled: Error: nested\n"
+        "    at throwError (<anonymous>:1:31)\n"
+        "    at callThrowError (<anonymous>:2:29)\n"
+        "    at <anonymous>:3:1";
+    EXPECT_EQ(kExpectedError, *logged_error);
+  }
+}
+
 }  // namespace extensions
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 9e04b94b..0bb39bb 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -686,6 +686,7 @@
 
   // Initialize or re-initialize the shader translator.
   bool InitializeShaderTranslator();
+  void DestroyShaderTranslator();
 
   void UpdateCapabilities();
 
@@ -1754,7 +1755,7 @@
       GLuint renderbuffer, GLenum format);
 
   // Wrapper for glReleaseShaderCompiler.
-  void DoReleaseShaderCompiler() { }
+  void DoReleaseShaderCompiler();
 
   // Wrappers for glSamplerParameter functions.
   void DoSamplerParameterf(GLuint client_id, GLenum pname, GLfloat param);
@@ -2375,6 +2376,7 @@
   // if not returning an error.
   error::Error current_decoder_error_;
 
+  bool has_fragment_precision_high_ = false;
   scoped_refptr<ShaderTranslatorInterface> vertex_translator_;
   scoped_refptr<ShaderTranslatorInterface> fragment_translator_;
 
@@ -3534,9 +3536,12 @@
                               features().khr_robustness ||
                               features().ext_robustness;
 
-  if (!InitializeShaderTranslator()) {
-    return false;
-  }
+  GLint range[2] = {0, 0};
+  GLint precision = 0;
+  QueryShaderPrecisionFormat(gl_version_info(), GL_FRAGMENT_SHADER,
+                             GL_HIGH_FLOAT, range, &precision);
+  has_fragment_precision_high_ =
+      PrecisionMeetsSpecForHighpFloat(range[0], range[1], precision);
 
   GLint viewport_params[4] = { 0 };
   glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_params);
@@ -3898,6 +3903,10 @@
   if (feature_info_->disable_shader_translator()) {
     return true;
   }
+  if (vertex_translator_ || fragment_translator_) {
+    DCHECK(vertex_translator_ && fragment_translator_);
+    return true;
+  }
   ShBuiltInResources resources;
   sh::InitBuiltInResources(&resources);
   resources.MaxVertexAttribs = group_->max_vertex_attribs();
@@ -3924,12 +3933,7 @@
     resources.MinProgramTexelOffset = group_->min_program_texel_offset();
   }
 
-  GLint range[2] = { 0, 0 };
-  GLint precision = 0;
-  QueryShaderPrecisionFormat(gl_version_info(), GL_FRAGMENT_SHADER,
-                             GL_HIGH_FLOAT, range, &precision);
-  resources.FragmentPrecisionHigh =
-      PrecisionMeetsSpecForHighpFloat(range[0], range[1], precision);
+  resources.FragmentPrecisionHigh = has_fragment_precision_high_;
 
   ShShaderSpec shader_spec;
   switch (feature_info_->context_type()) {
@@ -4048,6 +4052,11 @@
   return true;
 }
 
+void GLES2DecoderImpl::DestroyShaderTranslator() {
+  vertex_translator_ = nullptr;
+  fragment_translator_ = nullptr;
+}
+
 bool GLES2DecoderImpl::GenBuffersHelper(GLsizei n, const GLuint* client_ids) {
   for (GLsizei ii = 0; ii < n; ++ii) {
     if (GetBuffer(client_ids[ii])) {
@@ -4909,8 +4918,7 @@
 
   // Need to release these before releasing |group_| which may own the
   // ShaderTranslatorCache.
-  fragment_translator_ = NULL;
-  vertex_translator_ = NULL;
+  DestroyShaderTranslator();
 
   // Destroy the GPU Tracer which may own some in process GPU Timings.
   if (gpu_tracer_) {
@@ -8748,6 +8756,10 @@
       pixel[2] == 0xFF);
 }
 
+void GLES2DecoderImpl::DoReleaseShaderCompiler() {
+  DestroyShaderTranslator();
+}
+
 void GLES2DecoderImpl::DoRenderbufferStorage(
   GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
   Renderbuffer* renderbuffer =
@@ -10583,6 +10595,9 @@
 
 scoped_refptr<ShaderTranslatorInterface> GLES2DecoderImpl::GetTranslator(
     GLenum type) {
+  if (!InitializeShaderTranslator()) {
+    return nullptr;
+  }
   return type == GL_VERTEX_SHADER ? vertex_translator_ : fragment_translator_;
 }
 
@@ -15898,7 +15913,7 @@
     frag_depth_explicitly_enabled_ |= desire_frag_depth;
     draw_buffers_explicitly_enabled_ |= desire_draw_buffers;
     shader_texture_lod_explicitly_enabled_ |= desire_shader_texture_lod;
-    InitializeShaderTranslator();
+    DestroyShaderTranslator();
   }
 
   if (feature_str.find("GL_CHROMIUM_color_buffer_float_rgba ") !=
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index bda9206..6aa7196 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -1917,7 +1917,8 @@
 
   TestHelper::SetShaderStates(gl_.get(), GetShader(vertex_shader_client_id),
                               true, nullptr, nullptr, &shader_language_version_,
-                              nullptr, nullptr, nullptr, nullptr, nullptr);
+                              nullptr, nullptr, nullptr, nullptr, nullptr,
+                              nullptr);
 
   OutputVariableList frag_output_variable_list;
   frag_output_variable_list.push_back(TestHelper::ConstructOutputVariable(
@@ -1927,7 +1928,7 @@
   TestHelper::SetShaderStates(gl_.get(), GetShader(fragment_shader_client_id),
                               true, nullptr, nullptr, &shader_language_version_,
                               nullptr, nullptr, nullptr, nullptr,
-                              &frag_output_variable_list);
+                              &frag_output_variable_list, nullptr);
 
   cmds::AttachShader attach_cmd;
   attach_cmd.Init(program_client_id, vertex_shader_client_id);
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index b2c2f001..f51c5a3 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -149,11 +149,11 @@
     TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true, nullptr,
                                 nullptr, nullptr, &vertex_attrib_map,
                                 &vertex_uniform_map, &vertex_varying_map,
-                                nullptr, &vertex_output_variable_list);
-    TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true, nullptr,
-                                nullptr, nullptr, &fragment_attrib_map,
-                                &fragment_uniform_map, &fragment_varying_map,
-                                nullptr, &fragment_output_variable_list);
+                                nullptr, &vertex_output_variable_list, nullptr);
+    TestHelper::SetShaderStates(
+        gl_.get(), fragment_shader_, true, nullptr, nullptr, nullptr,
+        &fragment_attrib_map, &fragment_uniform_map, &fragment_varying_map,
+        nullptr, &fragment_output_variable_list, nullptr);
   }
 
   void SetExpectationsForSaveLinkedProgram(
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 25c23f0..1f88ea6 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -109,8 +109,9 @@
                           VaryingMap* varying_map,
                           InterfaceBlockMap* interface_block_map,
                           OutputVariableList* output_variable_list));
-  MOCK_CONST_METHOD0(
-      GetStringForOptionsThatWouldAffectCompilation, std::string());
+  MOCK_CONST_METHOD0(GetStringForOptionsThatWouldAffectCompilation,
+                     OptionsAffectingCompilationString*());
+
  private:
   ~MockShaderTranslator() override;
 };
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index e0269249..101d2784 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -29,6 +29,7 @@
 
 using ::testing::_;
 using ::testing::DoAll;
+using ::testing::Exactly;
 using ::testing::InSequence;
 using ::testing::MatcherCast;
 using ::testing::Pointee;
@@ -437,11 +438,11 @@
     TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                                 shader_version, &vertex_attrib_map,
                                 &vertex_uniform_map, &vertex_varying_map,
-                                nullptr, &vertex_output_variable_list);
+                                nullptr, &vertex_output_variable_list, nullptr);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 shader_version, &frag_attrib_map,
                                 &frag_uniform_map, &frag_varying_map, nullptr,
-                                &frag_output_variable_list);
+                                &frag_output_variable_list, nullptr);
 
     // Set up program
     Program* program =
@@ -942,13 +943,13 @@
   ASSERT_TRUE(vshader != NULL);
   TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                               nullptr, &attrib_map, &uniform_map, &varying_map,
-                              nullptr, &output_variable_list);
+                              nullptr, &output_variable_list, nullptr);
   Shader* fshader = shader_manager_.CreateShader(
       kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER);
   ASSERT_TRUE(fshader != NULL);
   TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                               nullptr, &attrib_map, &uniform_map, &varying_map,
-                              nullptr, &output_variable_list);
+                              nullptr, &output_variable_list, nullptr);
   static ProgramManagerWithShaderTest::AttribInfo kAttribs[] = {
     { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, },
     { kAttrib2Name, kAttrib2Size, kAttrib2BadType, kAttrib2Location, },
@@ -1612,7 +1613,7 @@
   // Set Status
   TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                               nullptr, &attrib_map, nullptr, nullptr, nullptr,
-                              nullptr);
+                              nullptr, nullptr);
   // Check attrib infos got copied.
   for (AttributeMap::const_iterator it = attrib_map.begin();
        it != attrib_map.end(); ++it) {
@@ -1627,7 +1628,7 @@
   }
   TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                               nullptr, &attrib_map, nullptr, nullptr, nullptr,
-                              nullptr);
+                              nullptr, nullptr);
   // Set up program
   Program* program =
       manager_->CreateProgram(kClientProgramId, kServiceProgramId);
@@ -1695,10 +1696,10 @@
   // Set Status
   TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                               nullptr, nullptr, &vertex_uniform_map, nullptr,
-                              nullptr, nullptr);
+                              nullptr, nullptr, nullptr);
   TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                               nullptr, nullptr, &frag_uniform_map, nullptr,
-                              nullptr, nullptr);
+                              nullptr, nullptr, nullptr);
   // Set up program
   Program* program =
       manager_->CreateProgram(kClientProgramId, kServiceProgramId);
@@ -1817,7 +1818,7 @@
       kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER);
   TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                               nullptr, nullptr, nullptr, nullptr, nullptr,
-                              nullptr);
+                              nullptr, nullptr);
   Shader* fshader = shader_manager_.CreateShader(
       kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER);
   ASSERT_TRUE(vshader && fshader);
@@ -1830,7 +1831,7 @@
   {  // No outputs.
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                nullptr);
+                                nullptr, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0u, program->fragment_output_type_mask());
     EXPECT_EQ(0u, program->fragment_output_written_mask());
@@ -1844,7 +1845,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0x3u, program->fragment_output_type_mask());
     EXPECT_EQ(0x3u, program->fragment_output_written_mask());
@@ -1858,7 +1859,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0xFFFFu, program->fragment_output_type_mask());
     EXPECT_EQ(0xFFFFu, program->fragment_output_written_mask());
@@ -1876,7 +1877,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0x3u, program->fragment_output_type_mask());
     EXPECT_EQ(0x3u, program->fragment_output_written_mask());
@@ -1890,7 +1891,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0x2u, program->fragment_output_type_mask());
     EXPECT_EQ(0x3u, program->fragment_output_written_mask());
@@ -1904,7 +1905,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0x2u, program->fragment_output_type_mask());
     EXPECT_EQ(0x3u, program->fragment_output_written_mask());
@@ -1922,7 +1923,7 @@
     fragment_outputs.push_back(var);
     TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                                 nullptr, nullptr, nullptr, nullptr, nullptr,
-                                &fragment_outputs);
+                                &fragment_outputs, nullptr);
     EXPECT_TRUE(LinkAsExpected(program, true));
     EXPECT_EQ(0xF1u, program->fragment_output_type_mask());
     EXPECT_EQ(0xF3u, program->fragment_output_written_mask());
@@ -2201,6 +2202,13 @@
     TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
   }
 
+  void SetShadersCompiled(const std::string& compilation_options_string) {
+    TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true,
+                                compilation_options_string);
+    TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true,
+                                compilation_options_string);
+  }
+
   void SetProgramCached() {
     cache_->LinkedProgramCacheSuccess(
         vertex_shader_->source(), fragment_shader_->source(),
@@ -2280,6 +2288,16 @@
         nullptr, 0, service_program_id);
   }
 
+  void SetExpectationsForProgramNotLoaded() {
+    EXPECT_CALL(*cache_.get(),
+                LoadLinkedProgram(
+                    program_->service_id(), vertex_shader_, fragment_shader_,
+                    &program_->bind_attrib_location_map(),
+                    program_->effective_transform_feedback_varyings(),
+                    program_->effective_transform_feedback_buffer_mode(), _))
+        .Times(Exactly(0));
+  }
+
   void SetExpectationsForProgramLink() {
     SetExpectationsForProgramLink(kServiceProgramId);
   }
@@ -2368,6 +2386,17 @@
   EXPECT_TRUE(program_->Link(NULL, Program::kCountOnlyStaticallyUsed, this));
 }
 
+TEST_F(ProgramManagerWithCacheTest, RelinkOnChangedCompileOptions) {
+  SetShadersCompiled("a");
+  SetProgramCached();
+  SetExpectationsForProgramCached();
+
+  SetShadersCompiled("b");
+  SetExpectationsForProgramLink();
+  SetExpectationsForProgramNotLoaded();
+  EXPECT_TRUE(program_->Link(NULL, Program::kCountOnlyStaticallyUsed, this));
+}
+
 class ProgramManagerWithPathRenderingTest
     : public ProgramManagerWithShaderTest,
       public testing::WithParamInterface<
@@ -2454,10 +2483,10 @@
       kFragmentInput3StaticUse, kFragmentInput3Name);
   TestHelper::SetShaderStates(gl_.get(), vshader, true, nullptr, nullptr,
                               nullptr, nullptr, nullptr, &varying_map, nullptr,
-                              nullptr);
+                              nullptr, nullptr);
   TestHelper::SetShaderStates(gl_.get(), fshader, true, nullptr, nullptr,
                               nullptr, nullptr, nullptr, &varying_map, nullptr,
-                              nullptr);
+                              nullptr, nullptr);
   Program* program =
       manager_->CreateProgram(kClientProgramId, kServiceProgramId);
   ASSERT_TRUE(program != NULL);
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 8a77b519..77e0df23e 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -51,6 +51,10 @@
                             TranslatedShaderSourceType type) {
   shader_state_ = kShaderStateCompileRequested;
   translator_ = translator;
+  if (translator_) {
+    options_affecting_compilation_ =
+        translator_->GetStringForOptionsThatWouldAffectCompilation();
+  }
   source_type_ = type;
   last_compiled_source_ = source_;
 }
@@ -121,6 +125,9 @@
         << "\n--translated-shader--\n" << source_for_driver
         << "\n--info-log--\n" << log_info_;
   }
+
+  // Translator is no longer required and can be released
+  translator_ = nullptr;
 }
 
 void Shader::RefreshTranslatedShaderSource() {
diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h
index c9c2df3..4df84f0 100644
--- a/gpu/command_buffer/service/shader_manager.h
+++ b/gpu/command_buffer/service/shader_manager.h
@@ -85,9 +85,8 @@
   }
 
   std::string last_compiled_signature() const {
-    if (translator_.get()) {
-      return last_compiled_source_ +
-             translator_->GetStringForOptionsThatWouldAffectCompilation();
+    if (options_affecting_compilation_) {
+      return last_compiled_source_ + options_affecting_compilation_->data;
     }
     return last_compiled_source_;
   }
@@ -235,6 +234,8 @@
 
   // Translator to use, set when shader was last requested to be compiled.
   scoped_refptr<ShaderTranslatorInterface> translator_;
+  scoped_refptr<OptionsAffectingCompilationString>
+      options_affecting_compilation_;
 
   // True if compilation succeeded.
   bool valid_;
diff --git a/gpu/command_buffer/service/shader_manager_unittest.cc b/gpu/command_buffer/service/shader_manager_unittest.cc
index bbf1470..9c552cb 100644
--- a/gpu/command_buffer/service/shader_manager_unittest.cc
+++ b/gpu/command_buffer/service/shader_manager_unittest.cc
@@ -220,9 +220,10 @@
           kInterfaceBlock1Name, kInterfaceBlock1InstanceName,
           interface_block1_fields);
 
-  TestHelper::SetShaderStates(
-      gl_.get(), shader1, true, &kLog, &kTranslatedSource, nullptr, &attrib_map,
-      &uniform_map, &varying_map, &interface_block_map, &output_variable_list);
+  TestHelper::SetShaderStates(gl_.get(), shader1, true, &kLog,
+                              &kTranslatedSource, nullptr, &attrib_map,
+                              &uniform_map, &varying_map, &interface_block_map,
+                              &output_variable_list, nullptr);
 
   EXPECT_TRUE(shader1->valid());
   // When compilation succeeds, no log is recorded.
@@ -325,9 +326,10 @@
   }
 
   // Compile failure case.
-  TestHelper::SetShaderStates(
-      gl_.get(), shader1, false, &kLog, &kTranslatedSource, nullptr,
-      &attrib_map, &uniform_map, &varying_map, nullptr, &output_variable_list);
+  TestHelper::SetShaderStates(gl_.get(), shader1, false, &kLog,
+                              &kTranslatedSource, nullptr, &attrib_map,
+                              &uniform_map, &varying_map, nullptr,
+                              &output_variable_list, nullptr);
   EXPECT_FALSE(shader1->valid());
   EXPECT_STREQ(kLog.c_str(), shader1->log_info().c_str());
   EXPECT_STREQ("", shader1->translated_source().c_str());
diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc
index 40f45e8..a22b679 100644
--- a/gpu/command_buffer/service/shader_translator.cc
+++ b/gpu/command_buffer/service/shader_translator.cc
@@ -185,6 +185,14 @@
       break;
   }
 
+  if (compiler_) {
+    options_affecting_compilation_ =
+        base::MakeRefCounted<OptionsAffectingCompilationString>(
+            std::string(":CompileOptions:" +
+                        base::Uint64ToString(GetCompileOptions())) +
+            sh::GetBuiltInResourcesString(compiler_));
+  }
+
   return compiler_ != NULL;
 }
 
@@ -237,12 +245,9 @@
   return success;
 }
 
-std::string ShaderTranslator::GetStringForOptionsThatWouldAffectCompilation()
-    const {
-  DCHECK(compiler_ != NULL);
-  return std::string(":CompileOptions:" +
-                     base::Uint64ToString(GetCompileOptions())) +
-         sh::GetBuiltInResourcesString(compiler_);
+OptionsAffectingCompilationString*
+ShaderTranslator::GetStringForOptionsThatWouldAffectCompilation() const {
+  return options_affecting_compilation_.get();
 }
 
 void ShaderTranslator::AddDestructionObserver(
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index 43ae7df7..a0a9c90e 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -27,6 +27,7 @@
 typedef base::hash_map<std::string, sh::Uniform> UniformMap;
 typedef base::hash_map<std::string, sh::Varying> VaryingMap;
 typedef base::hash_map<std::string, sh::InterfaceBlock> InterfaceBlockMap;
+typedef base::RefCountedData<std::string> OptionsAffectingCompilationString;
 
 // Translates a GLSL ES 2.0 shader to desktop GLSL shader, or just
 // validates GLSL ES 2.0 shaders on a true GLSL ES implementation.
@@ -61,7 +62,8 @@
 
   // Return a string that is unique for a specfic set of options that would
   // possibly affect compilation.
-  virtual std::string GetStringForOptionsThatWouldAffectCompilation() const = 0;
+  virtual OptionsAffectingCompilationString*
+  GetStringForOptionsThatWouldAffectCompilation() const = 0;
 
  protected:
   virtual ~ShaderTranslatorInterface() {}
@@ -111,7 +113,8 @@
                  InterfaceBlockMap* interface_block_map,
                  OutputVariableList* output_variable_list) const override;
 
-  std::string GetStringForOptionsThatWouldAffectCompilation() const override;
+  OptionsAffectingCompilationString*
+  GetStringForOptionsThatWouldAffectCompilation() const override;
 
   void AddDestructionObserver(DestructionObserver* observer);
   void RemoveDestructionObserver(DestructionObserver* observer);
@@ -123,6 +126,8 @@
 
   ShHandle compiler_;
   ShCompileOptions compile_options_;
+  scoped_refptr<OptionsAffectingCompilationString>
+      options_affecting_compilation_;
   base::ObserverList<DestructionObserver> destruction_observers_;
 };
 
diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc
index d9e0a418..6d2eb30 100644
--- a/gpu/command_buffer/service/shader_translator_unittest.cc
+++ b/gpu/command_buffer/service/shader_translator_unittest.cc
@@ -418,13 +418,13 @@
                                  false));
 
   std::string options_1(
-      translator_1->GetStringForOptionsThatWouldAffectCompilation());
+      translator_1->GetStringForOptionsThatWouldAffectCompilation()->data);
   std::string options_2(
-      translator_1->GetStringForOptionsThatWouldAffectCompilation());
+      translator_1->GetStringForOptionsThatWouldAffectCompilation()->data);
   std::string options_3(
-      translator_2->GetStringForOptionsThatWouldAffectCompilation());
+      translator_2->GetStringForOptionsThatWouldAffectCompilation()->data);
   std::string options_4(
-      translator_3->GetStringForOptionsThatWouldAffectCompilation());
+      translator_3->GetStringForOptionsThatWouldAffectCompilation()->data);
 
   EXPECT_EQ(options_1, options_2);
   EXPECT_NE(options_1, options_3);
diff --git a/gpu/command_buffer/service/test_helper.cc b/gpu/command_buffer/service/test_helper.cc
index 4b56f76..85315f05 100644
--- a/gpu/command_buffer/service/test_helper.cc
+++ b/gpu/command_buffer/service/test_helper.cc
@@ -1119,7 +1119,8 @@
     const UniformMap* const expected_uniform_map,
     const VaryingMap* const expected_varying_map,
     const InterfaceBlockMap* const expected_interface_block_map,
-    const OutputVariableList* const expected_output_variable_list) {
+    const OutputVariableList* const expected_output_variable_list,
+    OptionsAffectingCompilationString* options_affecting_compilation) {
   const std::string empty_log_info;
   const std::string* log_info = (expected_log_info && !expected_valid) ?
       expected_log_info : &empty_log_info;
@@ -1167,6 +1168,9 @@
           SetArgPointee<7>(*interface_block_map),
           SetArgPointee<8>(*output_variable_list), Return(expected_valid)))
       .RetiresOnSaturation();
+  EXPECT_CALL(*mock_translator, GetStringForOptionsThatWouldAffectCompilation())
+      .WillOnce(Return(options_affecting_compilation))
+      .RetiresOnSaturation();
   if (expected_valid) {
     EXPECT_CALL(*gl, ShaderSource(shader->service_id(), 1, _, NULL))
         .Times(1)
@@ -1188,7 +1192,20 @@
                                  Shader* shader,
                                  bool valid) {
   SetShaderStates(gl, shader, valid, nullptr, nullptr, nullptr, nullptr,
-                  nullptr, nullptr, nullptr, nullptr);
+                  nullptr, nullptr, nullptr, nullptr, nullptr);
+}
+
+// static
+void TestHelper::SetShaderStates(
+    ::gl::MockGLInterface* gl,
+    Shader* shader,
+    bool valid,
+    const std::string& options_affecting_compilation) {
+  scoped_refptr<OptionsAffectingCompilationString> options =
+      base::MakeRefCounted<OptionsAffectingCompilationString>(
+          options_affecting_compilation);
+  SetShaderStates(gl, shader, valid, nullptr, nullptr, nullptr, nullptr,
+                  nullptr, nullptr, nullptr, nullptr, options.get());
 }
 
 // static
diff --git a/gpu/command_buffer/service/test_helper.h b/gpu/command_buffer/service/test_helper.h
index f138507..7334064 100644
--- a/gpu/command_buffer/service/test_helper.h
+++ b/gpu/command_buffer/service/test_helper.h
@@ -197,12 +197,18 @@
       const UniformMap* const expected_uniform_map,
       const VaryingMap* const expected_varying_map,
       const InterfaceBlockMap* const expected_interface_block_map,
-      const OutputVariableList* const expected_output_variable_list);
+      const OutputVariableList* const expected_output_variable_list,
+      OptionsAffectingCompilationString* options_affecting_compilation);
 
   static void SetShaderStates(::gl::MockGLInterface* gl,
                               Shader* shader,
                               bool valid);
 
+  static void SetShaderStates(::gl::MockGLInterface* gl,
+                              Shader* shader,
+                              bool valid,
+                              const std::string& options_affecting_compilation);
+
   static sh::Attribute ConstructAttribute(
       GLenum type, GLint array_size, GLenum precision,
       bool static_use, const std::string& name);
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index c51404ca..67178ce 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -294,6 +294,7 @@
   }
 
   extra_substitutions = [
+    "CONTENT_WIDGET_EXTENSION_BUNDLE_ID=$chromium_bundle_id.ContentTodayExtension",
     "CHROMIUM_BUNDLE_ID=$chromium_bundle_id",
     "CHROMIUM_HANDOFF_ID=$chromium_handoff_id",
     "CHROMIUM_SHORT_NAME=$chromium_short_name",
diff --git a/ios/chrome/app/resources/Info.plist b/ios/chrome/app/resources/Info.plist
index 2a0bb7b..0e35e4e6 100644
--- a/ios/chrome/app/resources/Info.plist
+++ b/ios/chrome/app/resources/Info.plist
@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>UIApplicationShortcutWidget</key>
+	<string>${IOS_BUNDLE_ID_PREFIX}.${CONTENT_WIDGET_EXTENSION_BUNDLE_ID}</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>English</string>
 	<key>CFBundleDisplayName</key>
diff --git a/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni b/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
index 7623cb51..9778832 100644
--- a/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
+++ b/ios/chrome/test/earl_grey/chrome_ios_eg_test.gni
@@ -139,6 +139,7 @@
       "CHROMIUM_URL_SCHEME_4=$url_channel_scheme",
       "EG_MAIN_APPLICATION_DELEGATE=$_eg_main_application_delegate",
       "SSOAUTH_URL_SCHEME=$url_ssoauth_scheme",
+      "CONTENT_WIDGET_EXTENSION_BUNDLE_ID=$chromium_bundle_id.ContentTodayExtension",
     ]
     if (ios_automatically_manage_certs) {
       # Use the same bundle identifier for EarlGrey tests as for unit tests
diff --git a/ios/clean/chrome/app/BUILD.gn b/ios/clean/chrome/app/BUILD.gn
index e5d414ba..3e49a59 100644
--- a/ios/clean/chrome/app/BUILD.gn
+++ b/ios/clean/chrome/app/BUILD.gn
@@ -52,6 +52,7 @@
   bundle_deps = [ "//ios/chrome/app/resources" ]
 
   extra_substitutions = [
+    "CONTENT_WIDGET_EXTENSION_BUNDLE_ID=$chromium_bundle_id.ContentTodayExtension",
     "CHROMIUM_BUNDLE_ID=$output_name",
     "CHROMIUM_HANDOFF_ID=$chromium_handoff_id",
     "CHROMIUM_SHORT_NAME=$output_name",
diff --git a/ios/clean/chrome/test/perf/BUILD.gn b/ios/clean/chrome/test/perf/BUILD.gn
index d984a55a..0eedbf2 100644
--- a/ios/clean/chrome/test/perf/BUILD.gn
+++ b/ios/clean/chrome/test/perf/BUILD.gn
@@ -58,6 +58,7 @@
   _eg_main_application_delegate = "MainApplicationDelegate"
 
   extra_substitutions = [
+    "CONTENT_WIDGET_EXTENSION_BUNDLE_ID=$chromium_bundle_id.ContentTodayExtension",
     "CHROMIUM_HANDOFF_ID=$chromium_handoff_id",
     "CHROMIUM_SHORT_NAME=$target_name",
     "CHROMIUM_URL_SCHEME_1=$url_unsecure_scheme",
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 91dfe6d..6b5d0f108 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -958,11 +958,10 @@
   // not set to "null", a POST request from origin A to a malicious origin M
   // could be redirected by M back to A.
   //
-  // This behavior is specified in step 1 of step 10 of the 301, 302, 303, 307,
-  // 308 block of step 5 of Section 4.2 of Fetch[1] (which supercedes the
-  // behavior outlined in RFC 6454[2].
+  // This behavior is specified in step 10 of the HTTP-redirect fetch
+  // algorithm[1] (which supercedes the behavior outlined in RFC 6454[2].
   //
-  // [1]: https://fetch.spec.whatwg.org/#concept-http-fetch
+  // [1]: https://fetch.spec.whatwg.org/#http-redirect-fetch
   // [2]: https://tools.ietf.org/html/rfc6454#section-7
   //
   // TODO(jww): This is a layering violation and should be refactored somewhere
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index ac5a8c27..ae21dcd 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1772,6 +1772,7 @@
   pp::Point point = event.GetPosition();
   PDFiumPage::Area area =
       GetCharIndex(point, &page_index, &char_index, &form_type, &target);
+  DCHECK_GE(form_type, FPDF_FORMFIELD_UNKNOWN);
   mouse_down_state_.Set(area, target);
 
   // Decide whether to open link or not based on user action in mouse up and
@@ -1791,10 +1792,8 @@
 
     FPDF_PAGE page = pages_[page_index]->GetPage();
     FORM_OnLButtonDown(form_, page, 0, page_x, page_y);
-    if (form_type > FPDF_FORMFIELD_UNKNOWN) {  // returns -1 sometimes...
+    if (form_type != FPDF_FORMFIELD_UNKNOWN) {
       DCHECK_EQ(area, PDFiumPage::FormTypeToArea(form_type));
-      mouse_down_state_.Set(area, target);
-
       // Destroy SelectionChangeInvalidator object before SetInFormTextArea()
       // changes plugin's focus to be in form text area. This way, regular text
       // selection can be cleared when a user clicks into a form text area
diff --git a/services/ui/ws/server_window_compositor_frame_sink_manager.cc b/services/ui/ws/server_window_compositor_frame_sink_manager.cc
index aa5a769..e8fb206 100644
--- a/services/ui/ws/server_window_compositor_frame_sink_manager.cc
+++ b/services/ui/ws/server_window_compositor_frame_sink_manager.cc
@@ -7,12 +7,12 @@
 #include <utility>
 
 #include "base/command_line.h"
-#include "cc/base/switches.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/ui/ws/ids.h"
 #include "services/ui/ws/server_window.h"
 #include "services/ui/ws/server_window_delegate.h"
+#include "ui/base/ui_base_switches.h"
 
 namespace ui {
 namespace ws {
@@ -37,7 +37,7 @@
   viz::RendererSettings settings;
   settings.show_overdraw_feedback =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          cc::switches::kShowOverdrawFeedback);
+          switches::kShowOverdrawFeedback);
 
   // TODO(fsamuel): AcceleratedWidget cannot be transported over IPC for Mac
   // or Android. We should instead use GpuSurfaceTracker here on those
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 2767fe5..2315989 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -2644,6 +2644,67 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build13-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build13-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -3275,7 +3336,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -3306,7 +3367,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -3702,7 +3763,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -3733,7 +3794,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -7661,6 +7722,67 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build73-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build73-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -8292,7 +8414,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -8323,7 +8445,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -8719,7 +8841,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -8750,7 +8872,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -11428,6 +11550,37 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build165-b1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -11760,7 +11913,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -11977,7 +12130,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -15306,6 +15459,67 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build15-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build15-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -15937,7 +16151,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -15968,7 +16182,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -16364,7 +16578,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -16395,7 +16609,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -19104,6 +19318,37 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build113-b1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -19436,7 +19681,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -19653,7 +19898,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -22982,6 +23227,67 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build9-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build9-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -23613,7 +23919,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -23644,7 +23950,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -24040,7 +24346,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -24071,7 +24377,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -27877,6 +28183,67 @@
       },
       {
         "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build47-b1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "smoothness.gpu_rasterization_and_decoding.image_decoding_cases.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build47-b1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 72000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600,
+          "upload_test_results": false
+        }
+      },
+      {
+        "args": [
           "smoothness.key_desktop_move_cases",
           "-v",
           "--upload-results",
@@ -28508,7 +28875,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -28539,7 +28906,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
@@ -28935,7 +29302,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": false,
           "io_timeout": 3600,
@@ -28966,7 +29333,7 @@
               "pool": "Chrome-perf"
             }
           ],
-          "expiration": 36000,
+          "expiration": 72000,
           "hard_timeout": 10800,
           "ignore_task_failure": true,
           "io_timeout": 3600,
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index efbfe79..1a43507 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -18,7 +18,6 @@
 -FileProxyTest.SetTimes
 -FileUtilProxyTest.Touch
 -FileUtilTest.FileToFILE
--LoggingTest.CheckCausesDistinctBreakpoints
 -NativeLibraryTest.LoadLibrary
 -NativeLibraryTest.LoadLibraryPreferOwnSymbols
 -PathServiceTest.Get
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
index 5eba8bda..5a2d403 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
@@ -37,7 +37,6 @@
 #include "platform/bindings/SharedPersistent.h"
 #include "platform/heap/Handle.h"
 #include "platform/loader/fetch/AccessControlStatus.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/wtf/HashMap.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/Vector.h"
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 9951d500..5d886d9 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -188,6 +188,8 @@
     "FontFaceCache.h",
     "FontFaceSet.cpp",
     "FontFaceSet.h",
+    "FontFaceSetDocument.cpp",
+    "FontFaceSetDocument.h",
     "FontFaceSetLoadEvent.cpp",
     "FontFaceSetLoadEvent.h",
     "FontFaceSource.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSFontFace.cpp b/third_party/WebKit/Source/core/css/CSSFontFace.cpp
index f250769..aef7708 100644
--- a/third_party/WebKit/Source/core/css/CSSFontFace.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontFace.cpp
@@ -25,15 +25,15 @@
 
 #include "core/css/CSSFontFace.h"
 
+#include <algorithm>
 #include "core/css/CSSFontFaceSource.h"
 #include "core/css/CSSFontSelector.h"
 #include "core/css/CSSSegmentedFontFace.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/css/RemoteFontFaceSource.h"
 #include "core/frame/UseCounter.h"
 #include "platform/fonts/FontDescription.h"
 #include "platform/fonts/SimpleFontData.h"
-#include <algorithm>
 
 namespace blink {
 
@@ -187,7 +187,7 @@
     return;
   Document* document = ToDocument(font_face_->GetExecutionContext());
   if (document && new_status == FontFace::kLoading)
-    FontFaceSet::From(*document)->BeginFontLoading(font_face_);
+    FontFaceSetDocument::From(*document)->BeginFontLoading(font_face_);
 }
 
 DEFINE_TRACE(CSSFontFace) {
diff --git a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
index 5acadcd..ae5ac87a 100644
--- a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
@@ -29,7 +29,7 @@
 #include "build/build_config.h"
 #include "core/css/CSSSegmentedFontFace.h"
 #include "core/css/CSSValueList.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/css/resolver/StyleResolver.h"
 #include "core/dom/Document.h"
 #include "core/frame/LocalFrame.h"
@@ -54,7 +54,8 @@
   DCHECK(document_);
   DCHECK(document_->GetFrame());
   FontCache::GetFontCache()->AddClient(this);
-  FontFaceSet::From(*document)->AddFontFacesToFontFaceCache(&font_face_cache_);
+  FontFaceSetDocument::From(*document)->AddFontFacesToFontFaceCache(
+      &font_face_cache_);
 }
 
 CSSFontSelector::~CSSFontSelector() {}
diff --git a/third_party/WebKit/Source/core/css/FontFaceSet.cpp b/third_party/WebKit/Source/core/css/FontFaceSet.cpp
index 47b9f0e..2246f61 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSet.cpp
+++ b/third_party/WebKit/Source/core/css/FontFaceSet.cpp
@@ -1,582 +1,11 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- */
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 
 #include "core/css/FontFaceSet.h"
 
-#include "bindings/core/v8/Dictionary.h"
-#include "bindings/core/v8/ScriptPromiseResolver.h"
-#include "core/css/CSSFontSelector.h"
-#include "core/css/CSSSegmentedFontFace.h"
-#include "core/css/FontFaceCache.h"
-#include "core/css/FontFaceSetLoadEvent.h"
-#include "core/css/StylePropertySet.h"
-#include "core/css/parser/CSSParser.h"
-#include "core/css/resolver/StyleResolver.h"
-#include "core/dom/Document.h"
-#include "core/dom/StyleEngine.h"
-#include "core/frame/LocalFrame.h"
-#include "core/frame/LocalFrameView.h"
-#include "core/style/ComputedStyle.h"
-#include "platform/Histogram.h"
-#include "platform/bindings/ScriptState.h"
-
 namespace blink {
 
-static const int kDefaultFontSize = 10;
-static const char kDefaultFontFamily[] = "sans-serif";
-
-class LoadFontPromiseResolver final
-    : public GarbageCollectedFinalized<LoadFontPromiseResolver>,
-      public FontFace::LoadFontCallback {
-  USING_GARBAGE_COLLECTED_MIXIN(LoadFontPromiseResolver);
-
- public:
-  static LoadFontPromiseResolver* Create(FontFaceArray faces,
-                                         ScriptState* script_state) {
-    return new LoadFontPromiseResolver(faces, script_state);
-  }
-
-  void LoadFonts();
-  ScriptPromise Promise() { return resolver_->Promise(); }
-
-  void NotifyLoaded(FontFace*) override;
-  void NotifyError(FontFace*) override;
-
-  DECLARE_VIRTUAL_TRACE();
-
- private:
-  LoadFontPromiseResolver(FontFaceArray faces, ScriptState* script_state)
-      : num_loading_(faces.size()),
-        error_occured_(false),
-        resolver_(ScriptPromiseResolver::Create(script_state)) {
-    font_faces_.swap(faces);
-  }
-
-  HeapVector<Member<FontFace>> font_faces_;
-  int num_loading_;
-  bool error_occured_;
-  Member<ScriptPromiseResolver> resolver_;
-};
-
-void LoadFontPromiseResolver::LoadFonts() {
-  if (!num_loading_) {
-    resolver_->Resolve(font_faces_);
-    return;
-  }
-
-  for (size_t i = 0; i < font_faces_.size(); i++)
-    font_faces_[i]->LoadWithCallback(this);
-}
-
-void LoadFontPromiseResolver::NotifyLoaded(FontFace* font_face) {
-  num_loading_--;
-  if (num_loading_ || error_occured_)
-    return;
-
-  resolver_->Resolve(font_faces_);
-}
-
-void LoadFontPromiseResolver::NotifyError(FontFace* font_face) {
-  num_loading_--;
-  if (!error_occured_) {
-    error_occured_ = true;
-    resolver_->Reject(font_face->GetError());
-  }
-}
-
-DEFINE_TRACE(LoadFontPromiseResolver) {
-  visitor->Trace(font_faces_);
-  visitor->Trace(resolver_);
-  LoadFontCallback::Trace(visitor);
-}
-
-FontFaceSet::FontFaceSet(Document& document)
-    : Supplement<Document>(document),
-      SuspendableObject(&document),
-      should_fire_loading_event_(false),
-      is_loading_(false),
-      ready_(new ReadyProperty(GetExecutionContext(),
-                               this,
-                               ReadyProperty::kReady)),
-      async_runner_(AsyncMethodRunner<FontFaceSet>::Create(
-          this,
-          &FontFaceSet::HandlePendingEventsAndPromises)) {
-  SuspendIfNeeded();
-}
-
-FontFaceSet::~FontFaceSet() {}
-
-Document* FontFaceSet::GetDocument() const {
-  return ToDocument(GetExecutionContext());
-}
-
-bool FontFaceSet::InActiveDocumentContext() const {
-  ExecutionContext* context = GetExecutionContext();
-  return context && ToDocument(context)->IsActive();
-}
-
-void FontFaceSet::AddFontFacesToFontFaceCache(FontFaceCache* font_face_cache) {
-  for (const auto& font_face : non_css_connected_faces_)
-    font_face_cache->AddFontFace(font_face, false);
-}
-
-const AtomicString& FontFaceSet::InterfaceName() const {
-  return EventTargetNames::FontFaceSet;
-}
-
-ExecutionContext* FontFaceSet::GetExecutionContext() const {
-  return SuspendableObject::GetExecutionContext();
-}
-
-AtomicString FontFaceSet::status() const {
-  DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading"));
-  DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded"));
-  return is_loading_ ? loading : loaded;
-}
-
-void FontFaceSet::HandlePendingEventsAndPromisesSoon() {
-  // async_runner_ will be automatically stopped on destruction.
-  async_runner_->RunAsync();
-}
-
-void FontFaceSet::DidLayout() {
-  if (GetDocument()->GetFrame()->IsMainFrame() && loading_fonts_.IsEmpty())
-    histogram_.Record();
-  if (!ShouldSignalReady())
-    return;
-  HandlePendingEventsAndPromisesSoon();
-}
-
-bool FontFaceSet::ShouldSignalReady() const {
-  if (!loading_fonts_.IsEmpty())
-    return false;
-  return is_loading_ || ready_->GetState() == ReadyProperty::kPending;
-}
-
-void FontFaceSet::HandlePendingEventsAndPromises() {
-  FireLoadingEvent();
-  FireDoneEventIfPossible();
-}
-
-void FontFaceSet::FireLoadingEvent() {
-  if (should_fire_loading_event_) {
-    should_fire_loading_event_ = false;
-    DispatchEvent(
-        FontFaceSetLoadEvent::CreateForFontFaces(EventTypeNames::loading));
-  }
-}
-
-void FontFaceSet::Suspend() {
-  async_runner_->Suspend();
-}
-
-void FontFaceSet::Resume() {
-  async_runner_->Resume();
-}
-
-void FontFaceSet::ContextDestroyed(ExecutionContext*) {
-  async_runner_->Stop();
-}
-
-void FontFaceSet::BeginFontLoading(FontFace* font_face) {
-  histogram_.IncrementCount();
-  AddToLoadingFonts(font_face);
-}
-
-void FontFaceSet::NotifyLoaded(FontFace* font_face) {
-  histogram_.UpdateStatus(font_face);
-  loaded_fonts_.push_back(font_face);
-  RemoveFromLoadingFonts(font_face);
-}
-
-void FontFaceSet::NotifyError(FontFace* font_face) {
-  histogram_.UpdateStatus(font_face);
-  failed_fonts_.push_back(font_face);
-  RemoveFromLoadingFonts(font_face);
-}
-
-size_t FontFaceSet::ApproximateBlankCharacterCount() const {
-  size_t count = 0;
-  for (auto& font_face : loading_fonts_)
-    count += font_face->ApproximateBlankCharacterCount();
-  return count;
-}
-
-void FontFaceSet::AddToLoadingFonts(FontFace* font_face) {
-  if (!is_loading_) {
-    is_loading_ = true;
-    should_fire_loading_event_ = true;
-    if (ready_->GetState() != ReadyProperty::kPending)
-      ready_->Reset();
-    HandlePendingEventsAndPromisesSoon();
-  }
-  loading_fonts_.insert(font_face);
-  font_face->AddCallback(this);
-}
-
-void FontFaceSet::RemoveFromLoadingFonts(FontFace* font_face) {
-  loading_fonts_.erase(font_face);
-  if (loading_fonts_.IsEmpty())
-    HandlePendingEventsAndPromisesSoon();
-}
-
-ScriptPromise FontFaceSet::ready(ScriptState* script_state) {
-  if (ready_->GetState() != ReadyProperty::kPending &&
-      InActiveDocumentContext()) {
-    // |ready_| is already resolved, but there may be pending stylesheet
-    // changes and/or layout operations that may cause another font loads.
-    // So synchronously update style and layout here.
-    // This may trigger font loads, and replace |ready_| with a new Promise.
-    GetDocument()->UpdateStyleAndLayout();
-  }
-  return ready_->Promise(script_state->World());
-}
-
-FontFaceSet* FontFaceSet::addForBinding(ScriptState*,
-                                        FontFace* font_face,
-                                        ExceptionState&) {
-  DCHECK(font_face);
-  if (!InActiveDocumentContext())
-    return this;
-  if (non_css_connected_faces_.Contains(font_face))
-    return this;
-  if (IsCSSConnectedFontFace(font_face))
-    return this;
-  CSSFontSelector* font_selector =
-      GetDocument()->GetStyleEngine().GetFontSelector();
-  non_css_connected_faces_.insert(font_face);
-  font_selector->GetFontFaceCache()->AddFontFace(font_face, false);
-  if (font_face->LoadStatus() == FontFace::kLoading)
-    AddToLoadingFonts(font_face);
-  font_selector->FontFaceInvalidated();
-  return this;
-}
-
-void FontFaceSet::clearForBinding(ScriptState*, ExceptionState&) {
-  if (!InActiveDocumentContext() || non_css_connected_faces_.IsEmpty())
-    return;
-  CSSFontSelector* font_selector =
-      GetDocument()->GetStyleEngine().GetFontSelector();
-  FontFaceCache* font_face_cache = font_selector->GetFontFaceCache();
-  for (const auto& font_face : non_css_connected_faces_) {
-    font_face_cache->RemoveFontFace(font_face.Get(), false);
-    if (font_face->LoadStatus() == FontFace::kLoading)
-      RemoveFromLoadingFonts(font_face);
-  }
-  non_css_connected_faces_.clear();
-  font_selector->FontFaceInvalidated();
-}
-
-bool FontFaceSet::deleteForBinding(ScriptState*,
-                                   FontFace* font_face,
-                                   ExceptionState&) {
-  DCHECK(font_face);
-  if (!InActiveDocumentContext())
-    return false;
-  HeapListHashSet<Member<FontFace>>::iterator it =
-      non_css_connected_faces_.find(font_face);
-  if (it != non_css_connected_faces_.end()) {
-    non_css_connected_faces_.erase(it);
-    CSSFontSelector* font_selector =
-        GetDocument()->GetStyleEngine().GetFontSelector();
-    font_selector->GetFontFaceCache()->RemoveFontFace(font_face, false);
-    if (font_face->LoadStatus() == FontFace::kLoading)
-      RemoveFromLoadingFonts(font_face);
-    font_selector->FontFaceInvalidated();
-    return true;
-  }
-  return false;
-}
-
-bool FontFaceSet::hasForBinding(ScriptState*,
-                                FontFace* font_face,
-                                ExceptionState&) const {
-  DCHECK(font_face);
-  if (!InActiveDocumentContext())
-    return false;
-  return non_css_connected_faces_.Contains(font_face) ||
-         IsCSSConnectedFontFace(font_face);
-}
-
-const HeapListHashSet<Member<FontFace>>& FontFaceSet::CssConnectedFontFaceList()
-    const {
-  Document* document = this->GetDocument();
-  document->UpdateActiveStyle();
-  return document->GetStyleEngine()
-      .GetFontSelector()
-      ->GetFontFaceCache()
-      ->CssConnectedFontFaces();
-}
-
-bool FontFaceSet::IsCSSConnectedFontFace(FontFace* font_face) const {
-  return CssConnectedFontFaceList().Contains(font_face);
-}
-
-size_t FontFaceSet::size() const {
-  if (!InActiveDocumentContext())
-    return non_css_connected_faces_.size();
-  return CssConnectedFontFaceList().size() + non_css_connected_faces_.size();
-}
-
-void FontFaceSet::FireDoneEventIfPossible() {
-  if (should_fire_loading_event_)
-    return;
-  if (!ShouldSignalReady())
-    return;
-  Document* d = GetDocument();
-  if (!d)
-    return;
-
-  // If the layout was invalidated in between when we thought layout
-  // was updated and when we're ready to fire the event, just wait
-  // until after the next layout before firing events.
-  if (!d->View() || d->View()->NeedsLayout())
-    return;
-
-  if (is_loading_) {
-    FontFaceSetLoadEvent* done_event = nullptr;
-    FontFaceSetLoadEvent* error_event = nullptr;
-    done_event = FontFaceSetLoadEvent::CreateForFontFaces(
-        EventTypeNames::loadingdone, loaded_fonts_);
-    loaded_fonts_.clear();
-    if (!failed_fonts_.IsEmpty()) {
-      error_event = FontFaceSetLoadEvent::CreateForFontFaces(
-          EventTypeNames::loadingerror, failed_fonts_);
-      failed_fonts_.clear();
-    }
-    is_loading_ = false;
-    DispatchEvent(done_event);
-    if (error_event)
-      DispatchEvent(error_event);
-  }
-
-  if (ready_->GetState() == ReadyProperty::kPending)
-    ready_->Resolve(this);
-}
-
-ScriptPromise FontFaceSet::load(ScriptState* script_state,
-                                const String& font_string,
-                                const String& text) {
-  if (!InActiveDocumentContext())
-    return ScriptPromise();
-
-  Font font;
-  if (!ResolveFontStyle(font_string, font)) {
-    ScriptPromiseResolver* resolver =
-        ScriptPromiseResolver::Create(script_state);
-    ScriptPromise promise = resolver->Promise();
-    resolver->Reject(DOMException::Create(
-        kSyntaxError, "Could not resolve '" + font_string + "' as a font."));
-    return promise;
-  }
-
-  FontFaceCache* font_face_cache =
-      GetDocument()->GetStyleEngine().GetFontSelector()->GetFontFaceCache();
-  FontFaceArray faces;
-  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
-       f = f->Next()) {
-    CSSSegmentedFontFace* segmented_font_face =
-        font_face_cache->Get(font.GetFontDescription(), f->Family());
-    if (segmented_font_face)
-      segmented_font_face->Match(text, faces);
-  }
-
-  LoadFontPromiseResolver* resolver =
-      LoadFontPromiseResolver::Create(faces, script_state);
-  ScriptPromise promise = resolver->Promise();
-  // After this, resolver->promise() may return null.
-  resolver->LoadFonts();
-  return promise;
-}
-
-bool FontFaceSet::check(const String& font_string,
-                        const String& text,
-                        ExceptionState& exception_state) {
-  if (!InActiveDocumentContext())
-    return false;
-
-  Font font;
-  if (!ResolveFontStyle(font_string, font)) {
-    exception_state.ThrowDOMException(
-        kSyntaxError, "Could not resolve '" + font_string + "' as a font.");
-    return false;
-  }
-
-  CSSFontSelector* font_selector =
-      GetDocument()->GetStyleEngine().GetFontSelector();
-  FontFaceCache* font_face_cache = font_selector->GetFontFaceCache();
-
-  bool has_loaded_faces = false;
-  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
-       f = f->Next()) {
-    CSSSegmentedFontFace* face =
-        font_face_cache->Get(font.GetFontDescription(), f->Family());
-    if (face) {
-      if (!face->CheckFont(text))
-        return false;
-      has_loaded_faces = true;
-    }
-  }
-  if (has_loaded_faces)
-    return true;
-  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
-       f = f->Next()) {
-    if (font_selector->IsPlatformFamilyMatchAvailable(font.GetFontDescription(),
-                                                      f->Family()))
-      return true;
-  }
-  return false;
-}
-
-bool FontFaceSet::ResolveFontStyle(const String& font_string, Font& font) {
-  if (font_string.IsEmpty())
-    return false;
-
-  // Interpret fontString in the same way as the 'font' attribute of
-  // CanvasRenderingContext2D.
-  MutableStylePropertySet* parsed_style =
-      MutableStylePropertySet::Create(kHTMLStandardMode);
-  CSSParser::ParseValue(parsed_style, CSSPropertyFont, font_string, true);
-  if (parsed_style->IsEmpty())
-    return false;
-
-  String font_value = parsed_style->GetPropertyValue(CSSPropertyFont);
-  if (font_value == "inherit" || font_value == "initial")
-    return false;
-
-  RefPtr<ComputedStyle> style = ComputedStyle::Create();
-
-  FontFamily font_family;
-  font_family.SetFamily(kDefaultFontFamily);
-
-  FontDescription default_font_description;
-  default_font_description.SetFamily(font_family);
-  default_font_description.SetSpecifiedSize(kDefaultFontSize);
-  default_font_description.SetComputedSize(kDefaultFontSize);
-
-  style->SetFontDescription(default_font_description);
-
-  style->GetFont().Update(style->GetFont().GetFontSelector());
-
-  GetDocument()->UpdateActiveStyle();
-  GetDocument()->EnsureStyleResolver().ComputeFont(style.Get(), *parsed_style);
-
-  font = style->GetFont();
-  font.Update(GetDocument()->GetStyleEngine().GetFontSelector());
-  return true;
-}
-
-void FontFaceSet::FontLoadHistogram::UpdateStatus(FontFace* font_face) {
-  if (status_ == kReported)
-    return;
-  if (font_face->HadBlankText())
-    status_ = kHadBlankText;
-  else if (status_ == kNoWebFonts)
-    status_ = kDidNotHaveBlankText;
-}
-
-void FontFaceSet::FontLoadHistogram::Record() {
-  if (!recorded_) {
-    recorded_ = true;
-    DEFINE_STATIC_LOCAL(CustomCountHistogram, web_fonts_in_page_histogram,
-                        ("WebFont.WebFontsInPage", 1, 100, 50));
-    web_fonts_in_page_histogram.Count(count_);
-  }
-  if (status_ == kHadBlankText || status_ == kDidNotHaveBlankText) {
-    DEFINE_STATIC_LOCAL(EnumerationHistogram, had_blank_text_histogram,
-                        ("WebFont.HadBlankText", 2));
-    had_blank_text_histogram.Count(status_ == kHadBlankText ? 1 : 0);
-    status_ = kReported;
-  }
-}
-
-FontFaceSet* FontFaceSet::From(Document& document) {
-  FontFaceSet* fonts = static_cast<FontFaceSet*>(
-      Supplement<Document>::From(document, SupplementName()));
-  if (!fonts) {
-    fonts = FontFaceSet::Create(document);
-    Supplement<Document>::ProvideTo(document, SupplementName(), fonts);
-  }
-
-  return fonts;
-}
-
-void FontFaceSet::DidLayout(Document& document) {
-  if (FontFaceSet* fonts = static_cast<FontFaceSet*>(
-          Supplement<Document>::From(document, SupplementName())))
-    fonts->DidLayout();
-}
-
-size_t FontFaceSet::ApproximateBlankCharacterCount(Document& document) {
-  if (FontFaceSet* fonts = static_cast<FontFaceSet*>(
-          Supplement<Document>::From(document, SupplementName())))
-    return fonts->ApproximateBlankCharacterCount();
-  return 0;
-}
-
-FontFaceSetIterable::IterationSource* FontFaceSet::StartIteration(
-    ScriptState*,
-    ExceptionState&) {
-  // Setlike should iterate each item in insertion order, and items should
-  // be keep on up to date. But since blink does not have a way to hook up CSS
-  // modification, take a snapshot here, and make it ordered as follows.
-  HeapVector<Member<FontFace>> font_faces;
-  if (InActiveDocumentContext()) {
-    const HeapListHashSet<Member<FontFace>>& css_connected_faces =
-        CssConnectedFontFaceList();
-    font_faces.ReserveInitialCapacity(css_connected_faces.size() +
-                                      non_css_connected_faces_.size());
-    for (const auto& font_face : css_connected_faces)
-      font_faces.push_back(font_face);
-    for (const auto& font_face : non_css_connected_faces_)
-      font_faces.push_back(font_face);
-  }
-  return new IterationSource(font_faces);
-}
-
-bool FontFaceSet::IterationSource::Next(ScriptState*,
-                                        Member<FontFace>& key,
-                                        Member<FontFace>& value,
-                                        ExceptionState&) {
-  if (font_faces_.size() <= index_)
-    return false;
-  key = value = font_faces_[index_++];
-  return true;
-}
 
-DEFINE_TRACE(FontFaceSet) {
-  visitor->Trace(ready_);
-  visitor->Trace(loading_fonts_);
-  visitor->Trace(loaded_fonts_);
-  visitor->Trace(failed_fonts_);
-  visitor->Trace(non_css_connected_faces_);
-  visitor->Trace(async_runner_);
-  EventTargetWithInlineData::Trace(visitor);
-  Supplement<Document>::Trace(visitor);
-  SuspendableObject::Trace(visitor);
-  FontFace::LoadFontCallback::Trace(visitor);
-}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/FontFaceSet.h b/third_party/WebKit/Source/core/css/FontFaceSet.h
index 1114920..a9275645 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSet.h
+++ b/third_party/WebKit/Source/core/css/FontFaceSet.h
@@ -1,27 +1,6 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- */
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 
 #ifndef FontFaceSet_h
 #define FontFaceSet_h
@@ -29,14 +8,9 @@
 #include "bindings/core/v8/Iterable.h"
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/css/FontFace.h"
-#include "core/dom/Document.h"
-#include "core/dom/SuspendableObject.h"
 #include "core/events/EventListener.h"
 #include "core/events/EventTarget.h"
-#include "platform/AsyncMethodRunner.h"
-#include "platform/heap/Handle.h"
-#include "platform/wtf/Allocator.h"
-#include "platform/wtf/Vector.h"
+#include "platform/bindings/ScriptWrappable.h"
 
 // Mac OS X 10.6 SDK defines check() macro that interferes with our check()
 // method
@@ -46,79 +20,53 @@
 
 namespace blink {
 
-class ExceptionState;
-class Font;
-class FontFaceCache;
-class ExecutionContext;
-
 using FontFaceSetIterable = SetlikeIterable<Member<FontFace>>;
 
-class CORE_EXPORT FontFaceSet final : public EventTargetWithInlineData,
-                                      public Supplement<Document>,
-                                      public SuspendableObject,
-                                      public FontFaceSetIterable,
-                                      public FontFace::LoadFontCallback {
-  USING_GARBAGE_COLLECTED_MIXIN(FontFaceSet);
+class CORE_EXPORT FontFaceSet : public EventTargetWithInlineData,
+                                public FontFaceSetIterable {
   DEFINE_WRAPPERTYPEINFO();
   WTF_MAKE_NONCOPYABLE(FontFaceSet);
 
  public:
-  ~FontFaceSet() override;
+  FontFaceSet() {}
+  ~FontFaceSet() {}
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(loading);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(loadingdone);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(loadingerror);
 
-  bool check(const String& font, const String& text, ExceptionState&);
-  ScriptPromise load(ScriptState*, const String& font, const String& text);
-  ScriptPromise ready(ScriptState*);
+  virtual bool check(const String& font,
+                     const String& text,
+                     ExceptionState&) = 0;
+  virtual ScriptPromise load(ScriptState*,
+                             const String& font,
+                             const String& text) = 0;
+  virtual ScriptPromise ready(ScriptState*) = 0;
 
-  FontFaceSet* addForBinding(ScriptState*, FontFace*, ExceptionState&);
-  void clearForBinding(ScriptState*, ExceptionState&);
-  bool deleteForBinding(ScriptState*, FontFace*, ExceptionState&);
-  bool hasForBinding(ScriptState*, FontFace*, ExceptionState&) const;
-
-  size_t size() const;
-  AtomicString status() const;
-
-  ExecutionContext* GetExecutionContext() const override;
-  const AtomicString& InterfaceName() const override;
-
-  Document* GetDocument() const;
-
-  void DidLayout();
-  void BeginFontLoading(FontFace*);
-
-  // FontFace::LoadFontCallback
-  void NotifyLoaded(FontFace*) override;
-  void NotifyError(FontFace*) override;
-
-  size_t ApproximateBlankCharacterCount() const;
-
-  // SuspendableObject
-  void Suspend() override;
-  void Resume() override;
-  void ContextDestroyed(ExecutionContext*) override;
-
-  static FontFaceSet* From(Document&);
-  static void DidLayout(Document&);
-  static size_t ApproximateBlankCharacterCount(Document&);
-
-  static const char* SupplementName() { return "FontFaceSet"; }
-
-  void AddFontFacesToFontFaceCache(FontFaceCache*);
-
-  DECLARE_VIRTUAL_TRACE();
-
- private:
-  static FontFaceSet* Create(Document& document) {
-    return new FontFaceSet(document);
+  virtual ExecutionContext* GetExecutionContext() const = 0;
+  const AtomicString& InterfaceName() const {
+    return EventTargetNames::FontFaceSet;
   }
 
+  virtual FontFaceSet* addForBinding(ScriptState*,
+                                     FontFace*,
+                                     ExceptionState&) = 0;
+  virtual void clearForBinding(ScriptState*, ExceptionState&) = 0;
+  virtual bool deleteForBinding(ScriptState*, FontFace*, ExceptionState&) = 0;
+  virtual bool hasForBinding(ScriptState*,
+                             FontFace*,
+                             ExceptionState&) const = 0;
+
+  virtual size_t size() const = 0;
+  virtual AtomicString status() const = 0;
+
+  DEFINE_INLINE_VIRTUAL_TRACE() {}
+
+ protected:
   // Iterable overrides.
-  FontFaceSetIterable::IterationSource* StartIteration(
+  virtual FontFaceSetIterable::IterationSource* StartIteration(
       ScriptState*,
-      ExceptionState&) override;
+      ExceptionState&) = 0;
 
   class IterationSource final : public FontFaceSetIterable::IterationSource {
    public:
@@ -138,52 +86,6 @@
     size_t index_;
     HeapVector<Member<FontFace>> font_faces_;
   };
-
-  class FontLoadHistogram {
-    DISALLOW_NEW();
-
-   public:
-    enum Status { kNoWebFonts, kHadBlankText, kDidNotHaveBlankText, kReported };
-    FontLoadHistogram() : status_(kNoWebFonts), count_(0), recorded_(false) {}
-    void IncrementCount() { count_++; }
-    void UpdateStatus(FontFace*);
-    void Record();
-
-   private:
-    Status status_;
-    int count_;
-    bool recorded_;
-  };
-
-  explicit FontFaceSet(Document&);
-
-  bool InActiveDocumentContext() const;
-  void AddToLoadingFonts(FontFace*);
-  void RemoveFromLoadingFonts(FontFace*);
-  void FireLoadingEvent();
-  void FireDoneEventIfPossible();
-  bool ResolveFontStyle(const String&, Font&);
-  void HandlePendingEventsAndPromisesSoon();
-  void HandlePendingEventsAndPromises();
-  const HeapListHashSet<Member<FontFace>>& CssConnectedFontFaceList() const;
-  bool IsCSSConnectedFontFace(FontFace*) const;
-  bool ShouldSignalReady() const;
-
-  using ReadyProperty = ScriptPromiseProperty<Member<FontFaceSet>,
-                                              Member<FontFaceSet>,
-                                              Member<DOMException>>;
-
-  HeapHashSet<Member<FontFace>> loading_fonts_;
-  bool should_fire_loading_event_;
-  bool is_loading_;
-  Member<ReadyProperty> ready_;
-  FontFaceArray loaded_fonts_;
-  FontFaceArray failed_fonts_;
-  HeapListHashSet<Member<FontFace>> non_css_connected_faces_;
-
-  Member<AsyncMethodRunner<FontFaceSet>> async_runner_;
-
-  FontLoadHistogram histogram_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/FontFaceSetDocument.cpp b/third_party/WebKit/Source/core/css/FontFaceSetDocument.cpp
new file mode 100644
index 0000000..f480e0927
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/FontFaceSetDocument.cpp
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "core/css/FontFaceSetDocument.h"
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "core/css/CSSFontSelector.h"
+#include "core/css/CSSSegmentedFontFace.h"
+#include "core/css/FontFaceCache.h"
+#include "core/css/FontFaceSetLoadEvent.h"
+#include "core/css/StylePropertySet.h"
+#include "core/css/parser/CSSParser.h"
+#include "core/css/resolver/StyleResolver.h"
+#include "core/dom/Document.h"
+#include "core/dom/StyleEngine.h"
+#include "core/frame/LocalFrame.h"
+#include "core/frame/LocalFrameView.h"
+#include "core/style/ComputedStyle.h"
+#include "platform/Histogram.h"
+#include "platform/bindings/ScriptState.h"
+
+namespace blink {
+
+static const int kDefaultFontSize = 10;
+static const char kDefaultFontFamily[] = "sans-serif";
+
+class LoadFontPromiseResolver final
+    : public GarbageCollectedFinalized<LoadFontPromiseResolver>,
+      public FontFace::LoadFontCallback {
+  USING_GARBAGE_COLLECTED_MIXIN(LoadFontPromiseResolver);
+
+ public:
+  static LoadFontPromiseResolver* Create(FontFaceArray faces,
+                                         ScriptState* script_state) {
+    return new LoadFontPromiseResolver(faces, script_state);
+  }
+
+  void LoadFonts();
+  ScriptPromise Promise() { return resolver_->Promise(); }
+
+  void NotifyLoaded(FontFace*) override;
+  void NotifyError(FontFace*) override;
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  LoadFontPromiseResolver(FontFaceArray faces, ScriptState* script_state)
+      : num_loading_(faces.size()),
+        error_occured_(false),
+        resolver_(ScriptPromiseResolver::Create(script_state)) {
+    font_faces_.swap(faces);
+  }
+
+  HeapVector<Member<FontFace>> font_faces_;
+  int num_loading_;
+  bool error_occured_;
+  Member<ScriptPromiseResolver> resolver_;
+};
+
+void LoadFontPromiseResolver::LoadFonts() {
+  if (!num_loading_) {
+    resolver_->Resolve(font_faces_);
+    return;
+  }
+
+  for (size_t i = 0; i < font_faces_.size(); i++)
+    font_faces_[i]->LoadWithCallback(this);
+}
+
+void LoadFontPromiseResolver::NotifyLoaded(FontFace* font_face) {
+  num_loading_--;
+  if (num_loading_ || error_occured_)
+    return;
+
+  resolver_->Resolve(font_faces_);
+}
+
+void LoadFontPromiseResolver::NotifyError(FontFace* font_face) {
+  num_loading_--;
+  if (!error_occured_) {
+    error_occured_ = true;
+    resolver_->Reject(font_face->GetError());
+  }
+}
+
+DEFINE_TRACE(LoadFontPromiseResolver) {
+  visitor->Trace(font_faces_);
+  visitor->Trace(resolver_);
+  LoadFontCallback::Trace(visitor);
+}
+
+FontFaceSetDocument::FontFaceSetDocument(Document& document)
+    : Supplement<Document>(document),
+      SuspendableObject(&document),
+      should_fire_loading_event_(false),
+      is_loading_(false),
+      ready_(new ReadyProperty(GetExecutionContext(),
+                               this,
+                               ReadyProperty::kReady)),
+      async_runner_(AsyncMethodRunner<FontFaceSetDocument>::Create(
+          this,
+          &FontFaceSetDocument::HandlePendingEventsAndPromises)) {
+  SuspendIfNeeded();
+}
+
+FontFaceSetDocument::~FontFaceSetDocument() {}
+
+Document* FontFaceSetDocument::GetDocument() const {
+  return ToDocument(GetExecutionContext());
+}
+
+bool FontFaceSetDocument::InActiveDocumentContext() const {
+  ExecutionContext* context = GetExecutionContext();
+  return context && ToDocument(context)->IsActive();
+}
+
+void FontFaceSetDocument::AddFontFacesToFontFaceCache(
+    FontFaceCache* font_face_cache) {
+  for (const auto& font_face : non_css_connected_faces_)
+    font_face_cache->AddFontFace(font_face, false);
+}
+
+ExecutionContext* FontFaceSetDocument::GetExecutionContext() const {
+  return SuspendableObject::GetExecutionContext();
+}
+
+AtomicString FontFaceSetDocument::status() const {
+  DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading"));
+  DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded"));
+  return is_loading_ ? loading : loaded;
+}
+
+void FontFaceSetDocument::HandlePendingEventsAndPromisesSoon() {
+  // async_runner_ will be automatically stopped on destruction.
+  async_runner_->RunAsync();
+}
+
+void FontFaceSetDocument::DidLayout() {
+  if (GetDocument()->GetFrame()->IsMainFrame() && loading_fonts_.IsEmpty())
+    histogram_.Record();
+  if (!ShouldSignalReady())
+    return;
+  HandlePendingEventsAndPromisesSoon();
+}
+
+bool FontFaceSetDocument::ShouldSignalReady() const {
+  if (!loading_fonts_.IsEmpty())
+    return false;
+  return is_loading_ || ready_->GetState() == ReadyProperty::kPending;
+}
+
+void FontFaceSetDocument::HandlePendingEventsAndPromises() {
+  FireLoadingEvent();
+  FireDoneEventIfPossible();
+}
+
+void FontFaceSetDocument::FireLoadingEvent() {
+  if (should_fire_loading_event_) {
+    should_fire_loading_event_ = false;
+    DispatchEvent(
+        FontFaceSetLoadEvent::CreateForFontFaces(EventTypeNames::loading));
+  }
+}
+
+void FontFaceSetDocument::Suspend() {
+  async_runner_->Suspend();
+}
+
+void FontFaceSetDocument::Resume() {
+  async_runner_->Resume();
+}
+
+void FontFaceSetDocument::ContextDestroyed(ExecutionContext*) {
+  async_runner_->Stop();
+}
+
+void FontFaceSetDocument::BeginFontLoading(FontFace* font_face) {
+  histogram_.IncrementCount();
+  AddToLoadingFonts(font_face);
+}
+
+void FontFaceSetDocument::NotifyLoaded(FontFace* font_face) {
+  histogram_.UpdateStatus(font_face);
+  loaded_fonts_.push_back(font_face);
+  RemoveFromLoadingFonts(font_face);
+}
+
+void FontFaceSetDocument::NotifyError(FontFace* font_face) {
+  histogram_.UpdateStatus(font_face);
+  failed_fonts_.push_back(font_face);
+  RemoveFromLoadingFonts(font_face);
+}
+
+size_t FontFaceSetDocument::ApproximateBlankCharacterCount() const {
+  size_t count = 0;
+  for (auto& font_face : loading_fonts_)
+    count += font_face->ApproximateBlankCharacterCount();
+  return count;
+}
+
+void FontFaceSetDocument::AddToLoadingFonts(FontFace* font_face) {
+  if (!is_loading_) {
+    is_loading_ = true;
+    should_fire_loading_event_ = true;
+    if (ready_->GetState() != ReadyProperty::kPending)
+      ready_->Reset();
+    HandlePendingEventsAndPromisesSoon();
+  }
+  loading_fonts_.insert(font_face);
+  font_face->AddCallback(this);
+}
+
+void FontFaceSetDocument::RemoveFromLoadingFonts(FontFace* font_face) {
+  loading_fonts_.erase(font_face);
+  if (loading_fonts_.IsEmpty())
+    HandlePendingEventsAndPromisesSoon();
+}
+
+ScriptPromise FontFaceSetDocument::ready(ScriptState* script_state) {
+  if (ready_->GetState() != ReadyProperty::kPending &&
+      InActiveDocumentContext()) {
+    // |ready_| is already resolved, but there may be pending stylesheet
+    // changes and/or layout operations that may cause another font loads.
+    // So synchronously update style and layout here.
+    // This may trigger font loads, and replace |ready_| with a new Promise.
+    GetDocument()->UpdateStyleAndLayout();
+  }
+  return ready_->Promise(script_state->World());
+}
+
+FontFaceSet* FontFaceSetDocument::addForBinding(ScriptState*,
+                                                FontFace* font_face,
+                                                ExceptionState&) {
+  DCHECK(font_face);
+  if (!InActiveDocumentContext())
+    return this;
+  if (non_css_connected_faces_.Contains(font_face))
+    return this;
+  if (IsCSSConnectedFontFace(font_face))
+    return this;
+  CSSFontSelector* font_selector =
+      GetDocument()->GetStyleEngine().GetFontSelector();
+  non_css_connected_faces_.insert(font_face);
+  font_selector->GetFontFaceCache()->AddFontFace(font_face, false);
+  if (font_face->LoadStatus() == FontFace::kLoading)
+    AddToLoadingFonts(font_face);
+  font_selector->FontFaceInvalidated();
+  return this;
+}
+
+void FontFaceSetDocument::clearForBinding(ScriptState*, ExceptionState&) {
+  if (!InActiveDocumentContext() || non_css_connected_faces_.IsEmpty())
+    return;
+  CSSFontSelector* font_selector =
+      GetDocument()->GetStyleEngine().GetFontSelector();
+  FontFaceCache* font_face_cache = font_selector->GetFontFaceCache();
+  for (const auto& font_face : non_css_connected_faces_) {
+    font_face_cache->RemoveFontFace(font_face.Get(), false);
+    if (font_face->LoadStatus() == FontFace::kLoading)
+      RemoveFromLoadingFonts(font_face);
+  }
+  non_css_connected_faces_.clear();
+  font_selector->FontFaceInvalidated();
+}
+
+bool FontFaceSetDocument::deleteForBinding(ScriptState*,
+                                           FontFace* font_face,
+                                           ExceptionState&) {
+  DCHECK(font_face);
+  if (!InActiveDocumentContext())
+    return false;
+  HeapListHashSet<Member<FontFace>>::iterator it =
+      non_css_connected_faces_.find(font_face);
+  if (it != non_css_connected_faces_.end()) {
+    non_css_connected_faces_.erase(it);
+    CSSFontSelector* font_selector =
+        GetDocument()->GetStyleEngine().GetFontSelector();
+    font_selector->GetFontFaceCache()->RemoveFontFace(font_face, false);
+    if (font_face->LoadStatus() == FontFace::kLoading)
+      RemoveFromLoadingFonts(font_face);
+    font_selector->FontFaceInvalidated();
+    return true;
+  }
+  return false;
+}
+
+bool FontFaceSetDocument::hasForBinding(ScriptState*,
+                                        FontFace* font_face,
+                                        ExceptionState&) const {
+  DCHECK(font_face);
+  if (!InActiveDocumentContext())
+    return false;
+  return non_css_connected_faces_.Contains(font_face) ||
+         IsCSSConnectedFontFace(font_face);
+}
+
+const HeapListHashSet<Member<FontFace>>&
+FontFaceSetDocument::CssConnectedFontFaceList() const {
+  Document* document = this->GetDocument();
+  document->UpdateActiveStyle();
+  return document->GetStyleEngine()
+      .GetFontSelector()
+      ->GetFontFaceCache()
+      ->CssConnectedFontFaces();
+}
+
+bool FontFaceSetDocument::IsCSSConnectedFontFace(FontFace* font_face) const {
+  return CssConnectedFontFaceList().Contains(font_face);
+}
+
+size_t FontFaceSetDocument::size() const {
+  if (!InActiveDocumentContext())
+    return non_css_connected_faces_.size();
+  return CssConnectedFontFaceList().size() + non_css_connected_faces_.size();
+}
+
+void FontFaceSetDocument::FireDoneEventIfPossible() {
+  if (should_fire_loading_event_)
+    return;
+  if (!ShouldSignalReady())
+    return;
+  Document* d = GetDocument();
+  if (!d)
+    return;
+
+  // If the layout was invalidated in between when we thought layout
+  // was updated and when we're ready to fire the event, just wait
+  // until after the next layout before firing events.
+  if (!d->View() || d->View()->NeedsLayout())
+    return;
+
+  if (is_loading_) {
+    FontFaceSetLoadEvent* done_event = nullptr;
+    FontFaceSetLoadEvent* error_event = nullptr;
+    done_event = FontFaceSetLoadEvent::CreateForFontFaces(
+        EventTypeNames::loadingdone, loaded_fonts_);
+    loaded_fonts_.clear();
+    if (!failed_fonts_.IsEmpty()) {
+      error_event = FontFaceSetLoadEvent::CreateForFontFaces(
+          EventTypeNames::loadingerror, failed_fonts_);
+      failed_fonts_.clear();
+    }
+    is_loading_ = false;
+    DispatchEvent(done_event);
+    if (error_event)
+      DispatchEvent(error_event);
+  }
+
+  if (ready_->GetState() == ReadyProperty::kPending)
+    ready_->Resolve(this);
+}
+
+ScriptPromise FontFaceSetDocument::load(ScriptState* script_state,
+                                        const String& font_string,
+                                        const String& text) {
+  if (!InActiveDocumentContext())
+    return ScriptPromise();
+
+  Font font;
+  if (!ResolveFontStyle(font_string, font)) {
+    ScriptPromiseResolver* resolver =
+        ScriptPromiseResolver::Create(script_state);
+    ScriptPromise promise = resolver->Promise();
+    resolver->Reject(DOMException::Create(
+        kSyntaxError, "Could not resolve '" + font_string + "' as a font."));
+    return promise;
+  }
+
+  FontFaceCache* font_face_cache =
+      GetDocument()->GetStyleEngine().GetFontSelector()->GetFontFaceCache();
+  FontFaceArray faces;
+  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
+       f = f->Next()) {
+    CSSSegmentedFontFace* segmented_font_face =
+        font_face_cache->Get(font.GetFontDescription(), f->Family());
+    if (segmented_font_face)
+      segmented_font_face->Match(text, faces);
+  }
+
+  LoadFontPromiseResolver* resolver =
+      LoadFontPromiseResolver::Create(faces, script_state);
+  ScriptPromise promise = resolver->Promise();
+  // After this, resolver->promise() may return null.
+  resolver->LoadFonts();
+  return promise;
+}
+
+bool FontFaceSetDocument::check(const String& font_string,
+                                const String& text,
+                                ExceptionState& exception_state) {
+  if (!InActiveDocumentContext())
+    return false;
+
+  Font font;
+  if (!ResolveFontStyle(font_string, font)) {
+    exception_state.ThrowDOMException(
+        kSyntaxError, "Could not resolve '" + font_string + "' as a font.");
+    return false;
+  }
+
+  CSSFontSelector* font_selector =
+      GetDocument()->GetStyleEngine().GetFontSelector();
+  FontFaceCache* font_face_cache = font_selector->GetFontFaceCache();
+
+  bool has_loaded_faces = false;
+  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
+       f = f->Next()) {
+    CSSSegmentedFontFace* face =
+        font_face_cache->Get(font.GetFontDescription(), f->Family());
+    if (face) {
+      if (!face->CheckFont(text))
+        return false;
+      has_loaded_faces = true;
+    }
+  }
+  if (has_loaded_faces)
+    return true;
+  for (const FontFamily* f = &font.GetFontDescription().Family(); f;
+       f = f->Next()) {
+    if (font_selector->IsPlatformFamilyMatchAvailable(font.GetFontDescription(),
+                                                      f->Family()))
+      return true;
+  }
+  return false;
+}
+
+bool FontFaceSetDocument::ResolveFontStyle(const String& font_string,
+                                           Font& font) {
+  if (font_string.IsEmpty())
+    return false;
+
+  // Interpret fontString in the same way as the 'font' attribute of
+  // CanvasRenderingContext2D.
+  MutableStylePropertySet* parsed_style =
+      MutableStylePropertySet::Create(kHTMLStandardMode);
+  CSSParser::ParseValue(parsed_style, CSSPropertyFont, font_string, true);
+  if (parsed_style->IsEmpty())
+    return false;
+
+  String font_value = parsed_style->GetPropertyValue(CSSPropertyFont);
+  if (font_value == "inherit" || font_value == "initial")
+    return false;
+
+  RefPtr<ComputedStyle> style = ComputedStyle::Create();
+
+  FontFamily font_family;
+  font_family.SetFamily(kDefaultFontFamily);
+
+  FontDescription default_font_description;
+  default_font_description.SetFamily(font_family);
+  default_font_description.SetSpecifiedSize(kDefaultFontSize);
+  default_font_description.SetComputedSize(kDefaultFontSize);
+
+  style->SetFontDescription(default_font_description);
+
+  style->GetFont().Update(style->GetFont().GetFontSelector());
+
+  GetDocument()->UpdateActiveStyle();
+  GetDocument()->EnsureStyleResolver().ComputeFont(style.Get(), *parsed_style);
+
+  font = style->GetFont();
+  font.Update(GetDocument()->GetStyleEngine().GetFontSelector());
+  return true;
+}
+
+void FontFaceSetDocument::FontLoadHistogram::UpdateStatus(FontFace* font_face) {
+  if (status_ == kReported)
+    return;
+  if (font_face->HadBlankText())
+    status_ = kHadBlankText;
+  else if (status_ == kNoWebFonts)
+    status_ = kDidNotHaveBlankText;
+}
+
+void FontFaceSetDocument::FontLoadHistogram::Record() {
+  if (!recorded_) {
+    recorded_ = true;
+    DEFINE_STATIC_LOCAL(CustomCountHistogram, web_fonts_in_page_histogram,
+                        ("WebFont.WebFontsInPage", 1, 100, 50));
+    web_fonts_in_page_histogram.Count(count_);
+  }
+  if (status_ == kHadBlankText || status_ == kDidNotHaveBlankText) {
+    DEFINE_STATIC_LOCAL(EnumerationHistogram, had_blank_text_histogram,
+                        ("WebFont.HadBlankText", 2));
+    had_blank_text_histogram.Count(status_ == kHadBlankText ? 1 : 0);
+    status_ = kReported;
+  }
+}
+
+FontFaceSetDocument* FontFaceSetDocument::From(Document& document) {
+  FontFaceSetDocument* fonts = static_cast<FontFaceSetDocument*>(
+      Supplement<Document>::From(document, SupplementName()));
+  if (!fonts) {
+    fonts = FontFaceSetDocument::Create(document);
+    Supplement<Document>::ProvideTo(document, SupplementName(), fonts);
+  }
+
+  return fonts;
+}
+
+void FontFaceSetDocument::DidLayout(Document& document) {
+  if (FontFaceSetDocument* fonts = static_cast<FontFaceSetDocument*>(
+          Supplement<Document>::From(document, SupplementName())))
+    fonts->DidLayout();
+}
+
+size_t FontFaceSetDocument::ApproximateBlankCharacterCount(Document& document) {
+  if (FontFaceSetDocument* fonts = static_cast<FontFaceSetDocument*>(
+          Supplement<Document>::From(document, SupplementName())))
+    return fonts->ApproximateBlankCharacterCount();
+  return 0;
+}
+
+FontFaceSetIterable::IterationSource* FontFaceSetDocument::StartIteration(
+    ScriptState*,
+    ExceptionState&) {
+  // Setlike should iterate each item in insertion order, and items should
+  // be keep on up to date. But since blink does not have a way to hook up CSS
+  // modification, take a snapshot here, and make it ordered as follows.
+  HeapVector<Member<FontFace>> font_faces;
+  if (InActiveDocumentContext()) {
+    const HeapListHashSet<Member<FontFace>>& css_connected_faces =
+        CssConnectedFontFaceList();
+    font_faces.ReserveInitialCapacity(css_connected_faces.size() +
+                                      non_css_connected_faces_.size());
+    for (const auto& font_face : css_connected_faces)
+      font_faces.push_back(font_face);
+    for (const auto& font_face : non_css_connected_faces_)
+      font_faces.push_back(font_face);
+  }
+  return new IterationSource(font_faces);
+}
+
+bool FontFaceSetDocument::IterationSource::Next(ScriptState*,
+                                                Member<FontFace>& key,
+                                                Member<FontFace>& value,
+                                                ExceptionState&) {
+  if (font_faces_.size() <= index_)
+    return false;
+  key = value = font_faces_[index_++];
+  return true;
+}
+
+DEFINE_TRACE(FontFaceSetDocument) {
+  visitor->Trace(ready_);
+  visitor->Trace(loading_fonts_);
+  visitor->Trace(loaded_fonts_);
+  visitor->Trace(failed_fonts_);
+  visitor->Trace(non_css_connected_faces_);
+  visitor->Trace(async_runner_);
+  EventTargetWithInlineData::Trace(visitor);
+  Supplement<Document>::Trace(visitor);
+  SuspendableObject::Trace(visitor);
+  FontFace::LoadFontCallback::Trace(visitor);
+  FontFaceSet::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/FontFaceSetDocument.h b/third_party/WebKit/Source/core/css/FontFaceSetDocument.h
new file mode 100644
index 0000000..7ce4df7
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/FontFaceSetDocument.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef FontFaceSetDocument_h
+#define FontFaceSetDocument_h
+
+#include "bindings/core/v8/Iterable.h"
+#include "bindings/core/v8/ScriptPromise.h"
+#include "core/css/FontFace.h"
+#include "core/css/FontFaceSet.h"
+#include "core/dom/Document.h"
+#include "core/dom/SuspendableObject.h"
+#include "core/events/EventListener.h"
+#include "core/events/EventTarget.h"
+#include "platform/AsyncMethodRunner.h"
+#include "platform/heap/Handle.h"
+#include "platform/wtf/Allocator.h"
+#include "platform/wtf/Vector.h"
+
+// Mac OS X 10.6 SDK defines check() macro that interferes with our check()
+// method
+#ifdef check
+#undef check
+#endif
+
+namespace blink {
+
+class ExceptionState;
+class Font;
+class FontFaceCache;
+class ExecutionContext;
+
+class CORE_EXPORT FontFaceSetDocument final
+    : public FontFaceSet,
+      public Supplement<Document>,
+      public SuspendableObject,
+      public FontFace::LoadFontCallback {
+  USING_GARBAGE_COLLECTED_MIXIN(FontFaceSetDocument);
+  WTF_MAKE_NONCOPYABLE(FontFaceSetDocument);
+
+ public:
+  ~FontFaceSetDocument() override;
+
+  bool check(const String& font, const String& text, ExceptionState&) override;
+  ScriptPromise load(ScriptState*,
+                     const String& font,
+                     const String& text) override;
+  ScriptPromise ready(ScriptState*) override;
+
+  FontFaceSet* addForBinding(ScriptState*, FontFace*, ExceptionState&) override;
+  void clearForBinding(ScriptState*, ExceptionState&) override;
+  bool deleteForBinding(ScriptState*, FontFace*, ExceptionState&) override;
+  bool hasForBinding(ScriptState*, FontFace*, ExceptionState&) const override;
+
+  size_t size() const override;
+  AtomicString status() const override;
+
+  ExecutionContext* GetExecutionContext() const override;
+
+  Document* GetDocument() const;
+
+  void DidLayout();
+  void BeginFontLoading(FontFace*);
+
+  // FontFace::LoadFontCallback
+  void NotifyLoaded(FontFace*) override;
+  void NotifyError(FontFace*) override;
+
+  size_t ApproximateBlankCharacterCount() const;
+
+  // SuspendableObject
+  void Suspend() override;
+  void Resume() override;
+  void ContextDestroyed(ExecutionContext*) override;
+
+  static FontFaceSetDocument* From(Document&);
+  static void DidLayout(Document&);
+  static size_t ApproximateBlankCharacterCount(Document&);
+
+  static const char* SupplementName() { return "FontFaceSetDocument"; }
+
+  void AddFontFacesToFontFaceCache(FontFaceCache*);
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  FontFaceSetIterable::IterationSource* StartIteration(
+      ScriptState*,
+      ExceptionState&) override;
+
+  static FontFaceSetDocument* Create(Document& document) {
+    return new FontFaceSetDocument(document);
+  }
+
+  class FontLoadHistogram {
+    DISALLOW_NEW();
+
+   public:
+    enum Status { kNoWebFonts, kHadBlankText, kDidNotHaveBlankText, kReported };
+    FontLoadHistogram() : status_(kNoWebFonts), count_(0), recorded_(false) {}
+    void IncrementCount() { count_++; }
+    void UpdateStatus(FontFace*);
+    void Record();
+
+   private:
+    Status status_;
+    int count_;
+    bool recorded_;
+  };
+
+  explicit FontFaceSetDocument(Document&);
+
+  bool InActiveDocumentContext() const;
+  void AddToLoadingFonts(FontFace*);
+  void RemoveFromLoadingFonts(FontFace*);
+  void FireLoadingEvent();
+  void FireDoneEventIfPossible();
+  bool ResolveFontStyle(const String&, Font&);
+  void HandlePendingEventsAndPromisesSoon();
+  void HandlePendingEventsAndPromises();
+  const HeapListHashSet<Member<FontFace>>& CssConnectedFontFaceList() const;
+  bool IsCSSConnectedFontFace(FontFace*) const;
+  bool ShouldSignalReady() const;
+
+  using ReadyProperty = ScriptPromiseProperty<Member<FontFaceSetDocument>,
+                                              Member<FontFaceSetDocument>,
+                                              Member<DOMException>>;
+
+  HeapHashSet<Member<FontFace>> loading_fonts_;
+  bool should_fire_loading_event_;
+  bool is_loading_;
+  Member<ReadyProperty> ready_;
+  FontFaceArray loaded_fonts_;
+  FontFaceArray failed_fonts_;
+  HeapListHashSet<Member<FontFace>> non_css_connected_faces_;
+
+  Member<AsyncMethodRunner<FontFaceSetDocument>> async_runner_;
+
+  FontLoadHistogram histogram_;
+};
+
+}  // namespace blink
+
+#endif  // FontFaceSetDocument_h
diff --git a/third_party/WebKit/Source/core/css/FontFaceSource.cpp b/third_party/WebKit/Source/core/css/FontFaceSource.cpp
index 4d4dc4a..e059acd0 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSource.cpp
+++ b/third_party/WebKit/Source/core/css/FontFaceSource.cpp
@@ -4,12 +4,12 @@
 
 #include "core/css/FontFaceSource.h"
 
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 
 namespace blink {
 
 FontFaceSet* FontFaceSource::fonts(Document& document) {
-  return FontFaceSet::From(document);
+  return FontFaceSetDocument::From(document);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/OffscreenFontSelector.cpp b/third_party/WebKit/Source/core/css/OffscreenFontSelector.cpp
index 430d30ac..4a16fb18 100644
--- a/third_party/WebKit/Source/core/css/OffscreenFontSelector.cpp
+++ b/third_party/WebKit/Source/core/css/OffscreenFontSelector.cpp
@@ -7,7 +7,6 @@
 #include "build/build_config.h"
 #include "core/css/CSSSegmentedFontFace.h"
 #include "core/css/CSSValueList.h"
-#include "core/css/FontFaceSet.h"
 #include "core/css/resolver/StyleResolver.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Settings.h"
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 5fedacd..26ca7f2 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -54,7 +54,7 @@
 #include "core/css/CSSStyleDeclaration.h"
 #include "core/css/CSSStyleSheet.h"
 #include "core/css/CSSTiming.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/css/MediaQueryMatcher.h"
 #include "core/css/PropertyRegistry.h"
 #include "core/css/StylePropertySet.h"
@@ -6996,8 +6996,9 @@
   // Cannot trace in Supplementable<Document> as it is part of platform/ and
   // thus cannot refer to ScriptWrappableVisitor.
   visitor->TraceWrappersWithManualWriteBarrier(
-      static_cast<FontFaceSet*>(Supplementable<Document>::supplements_.at(
-          FontFaceSet::SupplementName())));
+      static_cast<FontFaceSetDocument*>(
+          Supplementable<Document>::supplements_.at(
+              FontFaceSetDocument::SupplementName())));
   ContainerNode::TraceWrappers(visitor);
 }
 
diff --git a/third_party/WebKit/Source/core/dom/RangeTest.cpp b/third_party/WebKit/Source/core/dom/RangeTest.cpp
index 219baeaa..64ca6ff 100644
--- a/third_party/WebKit/Source/core/dom/RangeTest.cpp
+++ b/third_party/WebKit/Source/core/dom/RangeTest.cpp
@@ -8,7 +8,7 @@
 #include "bindings/core/v8/StringOrArrayBufferOrArrayBufferView.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
 #include "core/css/FontFaceDescriptors.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/dom/Element.h"
 #include "core/dom/NodeList.h"
 #include "core/dom/Text.h"
@@ -271,8 +271,8 @@
   ScriptState* script_state =
       ToScriptStateForMainWorld(&page_holder.GetFrame());
   DummyExceptionStateForTesting exception_state;
-  FontFaceSet::From(document)->addForBinding(script_state, ahem,
-                                             exception_state);
+  FontFaceSetDocument::From(document)->addForBinding(script_state, ahem,
+                                                     exception_state);
 }
 
 TEST_F(RangeTest, BoundingRectMustIndependentFromSelection) {
diff --git a/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp b/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
index 15cafc71..10d296d 100644
--- a/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
@@ -41,7 +41,6 @@
 #include "platform/Timer.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/exported/WrappedResourceResponse.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/loader/fetch/ResourceError.h"
 #include "platform/loader/fetch/ResourceLoaderOptions.h"
@@ -50,6 +49,7 @@
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/WebHTTPHeaderVisitor.h"
 #include "public/platform/WebString.h"
 #include "public/platform/WebURLError.h"
@@ -222,14 +222,13 @@
     return;
   }
 
-  HTTPHeaderSet exposed_headers;
-  CrossOriginAccessControl::ExtractCorsExposedHeaderNamesList(response,
-                                                              exposed_headers);
-  HTTPHeaderSet blocked_headers;
+  WebCORS::HTTPHeaderSet exposed_headers;
+  WebCORS::ExtractCorsExposedHeaderNamesList(WrappedResourceResponse(response),
+                                             exposed_headers);
+  WebCORS::HTTPHeaderSet blocked_headers;
   for (const auto& header : response.HttpHeaderFields()) {
     if (FetchUtils::IsForbiddenResponseHeaderName(header.key) ||
-        (!CrossOriginAccessControl::IsOnAccessControlResponseHeaderWhitelist(
-             header.key) &&
+        (!WebCORS::IsOnAccessControlResponseHeaderWhitelist(header.key) &&
          !exposed_headers.Contains(header.key)))
       blocked_headers.insert(header.key);
   }
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index a55a5a4..89e4b9f 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -31,7 +31,7 @@
 #include "core/HTMLNames.h"
 #include "core/MediaTypeNames.h"
 #include "core/animation/DocumentAnimations.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/dom/AXObjectCache.h"
 #include "core/dom/DOMNodeIds.h"
 #include "core/dom/ElementVisibilityObserver.h"
@@ -1013,9 +1013,9 @@
   value->SetInteger("contentsHeightAfterLayout",
                     GetLayoutViewItem().DocumentRect().Height());
   value->SetInteger("visibleHeight", VisibleHeight());
-  value->SetInteger(
-      "approximateBlankCharacterCount",
-      FontFaceSet::ApproximateBlankCharacterCount(*frame_->GetDocument()));
+  value->SetInteger("approximateBlankCharacterCount",
+                    FontFaceSetDocument::ApproximateBlankCharacterCount(
+                        *frame_->GetDocument()));
   return value;
 }
 
@@ -2026,9 +2026,10 @@
     if (!layer->AncestorOverflowLayer())
       continue;
 
-    StickyConstraintsMap constraints_map = layer->AncestorOverflowLayer()
-                                               ->GetScrollableArea()
-                                               ->GetStickyConstraintsMap();
+    const StickyConstraintsMap& constraints_map =
+        layer->AncestorOverflowLayer()
+            ->GetScrollableArea()
+            ->GetStickyConstraintsMap();
     if (constraints_map.Contains(layer) &&
         !constraints_map.at(layer).HasAncestorStickyElement()) {
       // TODO(skobes): Resolve circular dependency between scroll offset and
@@ -2569,7 +2570,7 @@
 
   DCHECK(frame_->GetDocument());
 
-  FontFaceSet::DidLayout(*frame_->GetDocument());
+  FontFaceSetDocument::DidLayout(*frame_->GetDocument());
   // Cursor update scheduling is done by the local root, which is the main frame
   // if there are no RemoteFrame ancestors in the frame tree. Use of
   // localFrameRoot() is discouraged but will change when cursor update
diff --git a/third_party/WebKit/Source/core/layout/LayoutTestHelper.cpp b/third_party/WebKit/Source/core/layout/LayoutTestHelper.cpp
index f2687a8..7099fbc 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTestHelper.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTestHelper.cpp
@@ -7,7 +7,7 @@
 #include "bindings/core/v8/StringOrArrayBufferOrArrayBufferView.h"
 #include "bindings/core/v8/V8BindingForCore.h"
 #include "core/css/FontFaceDescriptors.h"
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/html/HTMLIFrameElement.h"
 #include "core/typed_arrays/DOMArrayBuffer.h"
 #include "platform/loader/fetch/MemoryCache.h"
@@ -89,7 +89,7 @@
   ScriptState* script_state =
       ToScriptStateForMainWorld(&page_holder_->GetFrame());
   DummyExceptionStateForTesting exception_state;
-  FontFaceSet::From(GetDocument())
+  FontFaceSetDocument::From(GetDocument())
       ->addForBinding(script_state, ahem, exception_state);
 }
 
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 439838f..ed7872e 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -298,9 +298,15 @@
 
 void CompositedLayerMapping::UpdateStickyConstraints(
     const ComputedStyle& style) {
+  WebLayerStickyPositionConstraint web_constraint;
+  if (!UsesCompositedStickyPosition()) {
+    // Clear the previous sticky position constraint - if set.
+    graphics_layer_->SetStickyPositionConstraint(web_constraint);
+    return;
+  }
+
   const PaintLayer* ancestor_overflow_layer =
       owning_layer_.AncestorOverflowLayer();
-  WebLayerStickyPositionConstraint web_constraint;
   const StickyConstraintsMap& constraints_map =
       ancestor_overflow_layer->GetScrollableArea()->GetStickyConstraintsMap();
   const StickyPositionScrollingConstraints& constraints =
@@ -1097,8 +1103,7 @@
                                           graphics_layer_parent_location);
   UpdateContentsOffsetInCompositingLayer(
       snapped_offset_from_composited_ancestor, graphics_layer_parent_location);
-  if (UsesCompositedStickyPosition())
-    UpdateStickyConstraints(GetLayoutObject().StyleRef());
+  UpdateStickyConstraints(GetLayoutObject().StyleRef());
   UpdateSquashingLayerGeometry(
       graphics_layer_parent_location, compositing_container, squashed_layers_,
       squashing_layer_.get(),
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
index 858bdc0..37da42e 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
@@ -2101,4 +2101,53 @@
       target_graphics_layer->ContentLayer()->TransformedRasterizationAllowed());
 }
 
+// This tests that when the scroller becomes no longer scrollable if a sticky
+// element is promoted for another reason we do remove its composited sticky
+// constraint as it doesn't need to move on the compositor.
+TEST_P(CompositedLayerMappingTest, CompositedStickyConstraintRemovedAndAdded) {
+  SetBodyInnerHTML(
+      "<style>"
+      ".scroller { overflow: auto; height: 200px; }"
+      ".sticky { position: sticky; top: 0; width: 10px; height: 10px; }"
+      ".composited { will-change: transform; }"
+      "</style>"
+      "<div class='composited scroller'>"
+      "  <div id='sticky' class='composited sticky'></div>"
+      "  <div id='spacer' style='height: 2000px;'></div>"
+      "</div>");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  PaintLayer* sticky_layer =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"))->Layer();
+  EXPECT_TRUE(sticky_layer->GraphicsLayerBacking()
+                  ->PlatformLayer()
+                  ->StickyPositionConstraint()
+                  .is_sticky);
+
+  // Make the scroller no longer scrollable.
+  GetDocument().getElementById("spacer")->setAttribute(HTMLNames::styleAttr,
+                                                       "height: 0;");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  // The sticky position element is composited due to a compositing trigger but
+  // should no longer have a sticky position constraint on the compositor.
+  sticky_layer =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"))->Layer();
+  EXPECT_FALSE(sticky_layer->GraphicsLayerBacking()
+                   ->PlatformLayer()
+                   ->StickyPositionConstraint()
+                   .is_sticky);
+
+  // Make the scroller scrollable again.
+  GetDocument().getElementById("spacer")->setAttribute(HTMLNames::styleAttr,
+                                                       "height: 2000px;");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  sticky_layer =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"))->Layer();
+  EXPECT_TRUE(sticky_layer->GraphicsLayerBacking()
+                  ->PlatformLayer()
+                  ->StickyPositionConstraint()
+                  .is_sticky);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
index a08bf3f7..7ba28314 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
@@ -50,7 +50,8 @@
 #include "core/page/Page.h"
 #include "core/probe/CoreProbes.h"
 #include "platform/SharedBuffer.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
+#include "platform/exported/WrappedResourceRequest.h"
+#include "platform/exported/WrappedResourceResponse.h"
 #include "platform/loader/fetch/FetchParameters.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/loader/fetch/Resource.h"
@@ -64,6 +65,8 @@
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/WeakPtr.h"
 #include "public/platform/Platform.h"
+#include "public/platform/WebCORS.h"
+#include "public/platform/WebSecurityOrigin.h"
 #include "public/platform/WebURLRequest.h"
 
 namespace blink {
@@ -348,9 +351,11 @@
 void DocumentThreadableLoader::LoadPreflightRequest(
     const ResourceRequest& actual_request,
     const ResourceLoaderOptions& actual_options) {
-  ResourceRequest preflight_request =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(
-          actual_request);
+  WebURLRequest web_url_request = WebCORS::CreateAccessControlPreflightRequest(
+      WrappedResourceRequest(actual_request));
+
+  ResourceRequest& preflight_request =
+      web_url_request.ToMutableResourceRequest();
 
   // TODO(tyoshino): Call prepareCrossOriginRequest(preflightRequest) to
   // also set the referrer header.
@@ -624,15 +629,14 @@
         redirect_response, resource);
   }
 
-  CrossOriginAccessControl::RedirectStatus redirect_status =
-      CrossOriginAccessControl::CheckRedirectLocation(new_url);
-  if (redirect_status != CrossOriginAccessControl::kRedirectSuccess) {
+  WebCORS::RedirectStatus redirect_status =
+      WebCORS::CheckRedirectLocation(new_url);
+  if (redirect_status != WebCORS::RedirectStatus::kRedirectSuccess) {
     StringBuilder builder;
     builder.Append("Redirect from '");
     builder.Append(original_url.GetString());
     builder.Append("' has been blocked by CORS policy: ");
-    CrossOriginAccessControl::RedirectErrorString(builder, redirect_status,
-                                                  new_url);
+    builder.Append(WebCORS::RedirectErrorString(redirect_status, new_url));
     DispatchDidFailAccessControlCheck(
         ResourceError::CancelledDueToAccessCheckError(
             original_url, ResourceRequestBlockedReason::kOther,
@@ -643,20 +647,20 @@
   if (cors_flag_) {
     // The redirect response must pass the access control check if the CORS
     // flag is set.
-    CrossOriginAccessControl::AccessStatus cors_status =
-        CrossOriginAccessControl::CheckAccess(
-            redirect_response, new_request.GetFetchCredentialsMode(),
-            GetSecurityOrigin());
-    if (cors_status != CrossOriginAccessControl::kAccessAllowed) {
+    WebCORS::AccessStatus cors_status =
+        WebCORS::CheckAccess(WrappedResourceResponse(redirect_response),
+                             new_request.GetFetchCredentialsMode(),
+                             WebSecurityOrigin(GetSecurityOrigin()));
+    if (cors_status != WebCORS::AccessStatus::kAccessAllowed) {
       StringBuilder builder;
       builder.Append("Redirect from '");
       builder.Append(original_url.GetString());
       builder.Append("' to '");
       builder.Append(new_url.GetString());
       builder.Append("' has been blocked by CORS policy: ");
-      CrossOriginAccessControl::AccessControlErrorString(
-          builder, cors_status, redirect_response, GetSecurityOrigin(),
-          request_context_);
+      builder.Append(WebCORS::AccessControlErrorString(
+          cors_status, WrappedResourceResponse(redirect_response),
+          WebSecurityOrigin(GetSecurityOrigin()), request_context_));
       DispatchDidFailAccessControlCheck(
           ResourceError::CancelledDueToAccessCheckError(
               original_url, ResourceRequestBlockedReason::kOther,
@@ -775,40 +779,41 @@
     const ResourceResponse& response) {
   String access_control_error_description;
 
-  CrossOriginAccessControl::AccessStatus cors_status =
-      CrossOriginAccessControl::CheckAccess(
-          response, actual_request_.GetFetchCredentialsMode(),
-          GetSecurityOrigin());
-  if (cors_status != CrossOriginAccessControl::kAccessAllowed) {
+  WebCORS::AccessStatus cors_status =
+      WebCORS::CheckAccess(WrappedResourceResponse(response),
+                           actual_request_.GetFetchCredentialsMode(),
+                           WebSecurityOrigin(GetSecurityOrigin()));
+  if (cors_status != WebCORS::AccessStatus::kAccessAllowed) {
     StringBuilder builder;
     builder.Append(
         "Response to preflight request doesn't pass access "
         "control check: ");
-    CrossOriginAccessControl::AccessControlErrorString(
-        builder, cors_status, response, GetSecurityOrigin(), request_context_);
+    builder.Append(WebCORS::AccessControlErrorString(
+        cors_status, WrappedResourceResponse(response),
+        WebSecurityOrigin(GetSecurityOrigin()), request_context_));
     HandlePreflightFailure(response.Url(), builder.ToString());
     return;
   }
 
-  CrossOriginAccessControl::PreflightStatus preflight_status =
-      CrossOriginAccessControl::CheckPreflight(response);
-  if (preflight_status != CrossOriginAccessControl::kPreflightSuccess) {
-    StringBuilder builder;
-    CrossOriginAccessControl::PreflightErrorString(builder, preflight_status,
-                                                   response);
-    HandlePreflightFailure(response.Url(), builder.ToString());
+  WebCORS::PreflightStatus preflight_status =
+      WebCORS::CheckPreflight(WrappedResourceResponse(response));
+  if (preflight_status != WebCORS::PreflightStatus::kPreflightSuccess) {
+    HandlePreflightFailure(
+        response.Url(),
+        WebCORS::PreflightErrorString(preflight_status,
+                                      WrappedResourceResponse(response)));
     return;
   }
 
   if (actual_request_.IsExternalRequest()) {
-    CrossOriginAccessControl::PreflightStatus external_preflight_status =
-        CrossOriginAccessControl::CheckExternalPreflight(response);
+    WebCORS::PreflightStatus external_preflight_status =
+        WebCORS::CheckExternalPreflight(WrappedResourceResponse(response));
     if (external_preflight_status !=
-        CrossOriginAccessControl::kPreflightSuccess) {
-      StringBuilder builder;
-      CrossOriginAccessControl::PreflightErrorString(
-          builder, external_preflight_status, response);
-      HandlePreflightFailure(response.Url(), builder.ToString());
+        WebCORS::PreflightStatus::kPreflightSuccess) {
+      HandlePreflightFailure(
+          response.Url(),
+          WebCORS::PreflightErrorString(external_preflight_status,
+                                        WrappedResourceResponse(response)));
       return;
     }
   }
@@ -886,9 +891,10 @@
         response.ResponseTypeViaServiceWorker() ==
             mojom::FetchResponseType::kOpaque) {
       StringBuilder builder;
-      CrossOriginAccessControl::AccessControlErrorString(
-          builder, CrossOriginAccessControl::kInvalidResponse, response,
-          GetSecurityOrigin(), request_context_);
+      builder.Append(WebCORS::AccessControlErrorString(
+          WebCORS::AccessStatus::kInvalidResponse,
+          WrappedResourceResponse(response),
+          WebSecurityOrigin(GetSecurityOrigin()), request_context_));
       DispatchDidFailAccessControlCheck(
           ResourceError::CancelledDueToAccessCheckError(
               response.Url(), ResourceRequestBlockedReason::kOther,
@@ -916,19 +922,17 @@
   fallback_request_for_service_worker_ = ResourceRequest();
 
   if (IsCORSEnabledRequestMode(request_mode) && cors_flag_) {
-    CrossOriginAccessControl::AccessStatus cors_status =
-        CrossOriginAccessControl::CheckAccess(response, credentials_mode,
-                                              GetSecurityOrigin());
-    if (cors_status != CrossOriginAccessControl::kAccessAllowed) {
+    WebCORS::AccessStatus cors_status = WebCORS::CheckAccess(
+        WrappedResourceResponse(response), credentials_mode,
+        WebSecurityOrigin(GetSecurityOrigin()));
+    if (cors_status != WebCORS::AccessStatus::kAccessAllowed) {
       ReportResponseReceived(identifier, response);
-      StringBuilder builder;
-      CrossOriginAccessControl::AccessControlErrorString(
-          builder, cors_status, response, GetSecurityOrigin(),
-          request_context_);
       DispatchDidFailAccessControlCheck(
           ResourceError::CancelledDueToAccessCheckError(
               response.Url(), ResourceRequestBlockedReason::kOther,
-              builder.ToString()));
+              WebCORS::AccessControlErrorString(
+                  cors_status, WrappedResourceResponse(response),
+                  WebSecurityOrigin(GetSecurityOrigin()), request_context_)));
       return;
     }
   }
@@ -1246,7 +1250,7 @@
   return !cors_flag_ && GetSecurityOrigin()->CanRequest(url);
 }
 
-const SecurityOrigin* DocumentThreadableLoader::GetSecurityOrigin() const {
+SecurityOrigin* DocumentThreadableLoader::GetSecurityOrigin() const {
   return security_origin_
              ? security_origin_.Get()
              : loading_context_->GetFetchContext()->GetSecurityOrigin();
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
index a1a1694a..521d0b4 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
@@ -193,7 +193,7 @@
   Member<RawResource> resource_;
   // End of ResourceOwner re-implementation, see above.
 
-  const SecurityOrigin* GetSecurityOrigin() const;
+  SecurityOrigin* GetSecurityOrigin() const;
 
   // TODO(kinuko): Remove dependency to document.
   Document* GetDocument() const;
diff --git a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
index 35e9ca8..f8f0f8c 100644
--- a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
+++ b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
@@ -265,7 +265,7 @@
 
     image_buffer_ = ImageBuffer::Create(std::move(surface));
 
-    if (needs_matrix_clip_restore_) {
+    if (image_buffer_ && needs_matrix_clip_restore_) {
       needs_matrix_clip_restore_ = false;
       context_->RestoreCanvasMatrixClipStack(image_buffer_->Canvas());
     }
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
index 3e80b9ab..1af03ea 100644
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
@@ -4,7 +4,7 @@
 
 #include "core/paint/FirstMeaningfulPaintDetector.h"
 
-#include "core/css/FontFaceSet.h"
+#include "core/css/FontFaceSetDocument.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/paint/PaintTiming.h"
 #include "platform/Histogram.h"
@@ -72,7 +72,7 @@
   // If the page has many blank characters, the significance value is
   // accumulated until the text become visible.
   int approximate_blank_character_count =
-      FontFaceSet::ApproximateBlankCharacterCount(*GetDocument());
+      FontFaceSetDocument::ApproximateBlankCharacterCount(*GetDocument());
   if (approximate_blank_character_count > kBlankCharactersThreshold) {
     accumulated_significance_while_having_blank_text_ += significance;
   } else {
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index 4d29d09..de90dc0 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -65,7 +65,7 @@
 #include "platform/bindings/DOMWrapperWorld.h"
 #include "platform/bindings/ScriptState.h"
 #include "platform/blob/BlobData.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
+#include "platform/exported/WrappedResourceResponse.h"
 #include "platform/loader/fetch/FetchInitiatorTypeNames.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/loader/fetch/ResourceError.h"
@@ -81,6 +81,7 @@
 #include "platform/wtf/AutoReset.h"
 #include "platform/wtf/StdLibExtras.h"
 #include "platform/wtf/text/CString.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/WebURLRequest.h"
 
 namespace blink {
@@ -1413,9 +1414,9 @@
 
   StringBuilder string_builder;
 
-  HTTPHeaderSet access_control_expose_header_set;
-  CrossOriginAccessControl::ExtractCorsExposedHeaderNamesList(
-      response_, access_control_expose_header_set);
+  WebCORS::HTTPHeaderSet access_control_expose_header_set;
+  WebCORS::ExtractCorsExposedHeaderNamesList(WrappedResourceResponse(response_),
+                                             access_control_expose_header_set);
 
   HTTPHeaderMap::const_iterator end = response_.HttpHeaderFields().end();
   for (HTTPHeaderMap::const_iterator it = response_.HttpHeaderFields().begin();
@@ -1430,8 +1431,7 @@
       continue;
 
     if (!same_origin_request_ &&
-        !CrossOriginAccessControl::IsOnAccessControlResponseHeaderWhitelist(
-            it->key) &&
+        !WebCORS::IsOnAccessControlResponseHeaderWhitelist(it->key) &&
         !access_control_expose_header_set.Contains(it->key))
       continue;
 
@@ -1459,13 +1459,12 @@
     return g_null_atom;
   }
 
-  HTTPHeaderSet access_control_expose_header_set;
-  CrossOriginAccessControl::ExtractCorsExposedHeaderNamesList(
-      response_, access_control_expose_header_set);
+  WebCORS::HTTPHeaderSet access_control_expose_header_set;
+  WebCORS::ExtractCorsExposedHeaderNamesList(WrappedResourceResponse(response_),
+                                             access_control_expose_header_set);
 
   if (!same_origin_request_ &&
-      !CrossOriginAccessControl::IsOnAccessControlResponseHeaderWhitelist(
-          name) &&
+      !WebCORS::IsOnAccessControlResponseHeaderWhitelist(name) &&
       !access_control_expose_header_set.Contains(name)) {
     LogConsoleError(GetExecutionContext(),
                     "Refused to get unsafe header \"" + name + "\"");
diff --git a/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp b/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
index d02266709..f9ea1d07 100644
--- a/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
+++ b/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
@@ -14,6 +14,7 @@
 #include "modules/cachestorage/CacheStorageError.h"
 #include "modules/fetch/Request.h"
 #include "modules/fetch/Response.h"
+#include "platform/HTTPNames.h"
 #include "platform/bindings/ScriptState.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerCacheError.h"
diff --git a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
index 47ddb452..e6c2fdc 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
@@ -31,8 +31,8 @@
 #include "platform/HTTPNames.h"
 #include "platform/bindings/ScriptState.h"
 #include "platform/bindings/V8ThrowException.h"
+#include "platform/exported/WrappedResourceResponse.h"
 #include "platform/loader/SubresourceIntegrity.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/loader/fetch/ResourceError.h"
 #include "platform/loader/fetch/ResourceLoaderOptions.h"
@@ -46,6 +46,7 @@
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/WebURLRequest.h"
 
 namespace blink {
@@ -468,9 +469,9 @@
         tainted_response = response_data->CreateBasicFilteredResponse();
         break;
       case FetchRequestData::kCORSTainting: {
-        HTTPHeaderSet header_names;
-        CrossOriginAccessControl::ExtractCorsExposedHeaderNamesList(
-            response, header_names);
+        WebCORS::HTTPHeaderSet header_names;
+        WebCORS::ExtractCorsExposedHeaderNamesList(
+            WrappedResourceResponse(response), header_names);
         tainted_response =
             response_data->CreateCORSFilteredResponse(header_names);
         break;
diff --git a/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp b/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
index f0a13e0..b8a4d21 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
@@ -7,8 +7,8 @@
 #include "core/typed_arrays/DOMArrayBuffer.h"
 #include "modules/fetch/BodyStreamBuffer.h"
 #include "modules/fetch/FetchHeaderList.h"
+#include "platform/HTTPNames.h"
 #include "platform/bindings/ScriptState.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/FetchUtils.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
@@ -43,7 +43,8 @@
   return web_type;
 }
 
-WebVector<WebString> HeaderSetToWebVector(const HTTPHeaderSet& headers) {
+WebVector<WebString> HeaderSetToWebVector(
+    const WebCORS::HTTPHeaderSet& headers) {
   // Can't just pass *headers to the WebVector constructor because HashSet
   // iterators are not stl iterator compatible.
   WebVector<WebString> result(static_cast<size_t>(headers.size()));
@@ -96,18 +97,18 @@
 
 FetchResponseData* FetchResponseData::CreateCORSFilteredResponse() const {
   DCHECK_EQ(type_, kDefaultType);
-  HTTPHeaderSet access_control_expose_header_set;
+  WebCORS::HTTPHeaderSet access_control_expose_header_set;
   String access_control_expose_headers;
   if (header_list_->Get(HTTPNames::Access_Control_Expose_Headers,
                         access_control_expose_headers)) {
-    CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
+    WebCORS::ParseAccessControlExposeHeadersAllowList(
         access_control_expose_headers, access_control_expose_header_set);
   }
   return CreateCORSFilteredResponse(access_control_expose_header_set);
 }
 
 FetchResponseData* FetchResponseData::CreateCORSFilteredResponse(
-    const HTTPHeaderSet& exposed_headers) const {
+    const WebCORS::HTTPHeaderSet& exposed_headers) const {
   DCHECK_EQ(type_, kDefaultType);
   // "A CORS filtered response is a filtered response whose type is |CORS|,
   // header list excludes all headers in internal response's header list,
@@ -122,8 +123,7 @@
   for (const auto& header : header_list_->List()) {
     const String& name = header.first;
     const bool explicitly_exposed = exposed_headers.Contains(name);
-    if (CrossOriginAccessControl::IsOnAccessControlResponseHeaderWhitelist(
-            name) ||
+    if (WebCORS::IsOnAccessControlResponseHeaderWhitelist(name) ||
         (explicitly_exposed &&
          !FetchUtils::IsForbiddenResponseHeaderName(name))) {
       if (explicitly_exposed)
diff --git a/third_party/WebKit/Source/modules/fetch/FetchResponseData.h b/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
index 707814c..88a9fd04 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
+++ b/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
@@ -8,12 +8,12 @@
 #include <memory>
 #include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/weborigin/KURL.h"
 #include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/Time.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/AtomicString.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/modules/fetch/fetch_api_request.mojom-blink.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
 
@@ -60,7 +60,7 @@
   // Creates a CORS filtered response with an explicit set of exposed header
   // names.
   FetchResponseData* CreateCORSFilteredResponse(
-      const HTTPHeaderSet& exposed_headers) const;
+      const WebCORS::HTTPHeaderSet& exposed_headers) const;
   FetchResponseData* CreateOpaqueFilteredResponse() const;
   FetchResponseData* CreateOpaqueRedirectFilteredResponse() const;
 
@@ -84,7 +84,7 @@
   String InternalMIMEType() const;
   Time ResponseTime() const { return response_time_; }
   String CacheStorageCacheName() const { return cache_storage_cache_name_; }
-  const HTTPHeaderSet& CorsExposedHeaderNames() const {
+  const WebCORS::HTTPHeaderSet& CorsExposedHeaderNames() const {
     return cors_exposed_header_names_;
   }
 
@@ -101,7 +101,7 @@
   void SetCacheStorageCacheName(const String& cache_storage_cache_name) {
     cache_storage_cache_name_ = cache_storage_cache_name;
   }
-  void SetCorsExposedHeaderNames(const HTTPHeaderSet& header_names) {
+  void SetCorsExposedHeaderNames(const WebCORS::HTTPHeaderSet& header_names) {
     cors_exposed_header_names_ = header_names;
   }
 
@@ -132,7 +132,7 @@
   String mime_type_;
   Time response_time_;
   String cache_storage_cache_name_;
-  HTTPHeaderSet cors_exposed_header_names_;
+  WebCORS::HTTPHeaderSet cors_exposed_header_names_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/fetch/FetchResponseDataTest.cpp b/third_party/WebKit/Source/modules/fetch/FetchResponseDataTest.cpp
index e65f4ca..8d1bdeb 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchResponseDataTest.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchResponseDataTest.cpp
@@ -135,7 +135,7 @@
 TEST_F(FetchResponseDataTest, CORSFilterWithEmptyHeaderSet) {
   FetchResponseData* internal_response = CreateInternalResponse();
   FetchResponseData* cors_response_data =
-      internal_response->CreateCORSFilteredResponse(HTTPHeaderSet());
+      internal_response->CreateCORSFilteredResponse(WebCORS::HTTPHeaderSet());
 
   EXPECT_EQ(internal_response, cors_response_data->InternalResponse());
 
@@ -157,7 +157,7 @@
                                           "set-cookie, bar");
 
   FetchResponseData* cors_response_data =
-      internal_response->CreateCORSFilteredResponse(HTTPHeaderSet());
+      internal_response->CreateCORSFilteredResponse(WebCORS::HTTPHeaderSet());
 
   EXPECT_EQ(internal_response, cors_response_data->InternalResponse());
 
@@ -174,7 +174,7 @@
 
 TEST_F(FetchResponseDataTest, CORSFilterWithExplicitHeaderSet) {
   FetchResponseData* internal_response = CreateInternalResponse();
-  HTTPHeaderSet exposed_headers;
+  WebCORS::HTTPHeaderSet exposed_headers;
   exposed_headers.insert("set-cookie");
   exposed_headers.insert("bar");
 
diff --git a/third_party/WebKit/Source/modules/fetch/Response.cpp b/third_party/WebKit/Source/modules/fetch/Response.cpp
index 2fe272f5..84fc53f 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Response.cpp
@@ -25,6 +25,7 @@
 #include "modules/fetch/BodyStreamBuffer.h"
 #include "modules/fetch/FormDataBytesConsumer.h"
 #include "modules/fetch/ResponseInit.h"
+#include "platform/HTTPNames.h"
 #include "platform/bindings/ScriptState.h"
 #include "platform/bindings/V8PrivateProperty.h"
 #include "platform/loader/fetch/FetchUtils.h"
@@ -32,6 +33,7 @@
 #include "platform/network/HTTPHeaderMap.h"
 #include "platform/network/NetworkUtils.h"
 #include "platform/wtf/RefPtr.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
 
 namespace blink {
@@ -73,7 +75,7 @@
       response = response->CreateBasicFilteredResponse();
       break;
     case mojom::FetchResponseType::kCORS: {
-      HTTPHeaderSet header_names;
+      WebCORS::HTTPHeaderSet header_names;
       for (const auto& header : web_response.CorsExposedHeaderNames())
         header_names.insert(String(header));
       response = response->CreateCORSFilteredResponse(header_names);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ForeignFetchRespondWithObserver.cpp b/third_party/WebKit/Source/modules/serviceworkers/ForeignFetchRespondWithObserver.cpp
index 3c4a4deb..a3e96e3 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ForeignFetchRespondWithObserver.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ForeignFetchRespondWithObserver.cpp
@@ -7,7 +7,7 @@
 #include "bindings/modules/v8/V8ForeignFetchResponse.h"
 #include "modules/fetch/Response.h"
 #include "modules/serviceworkers/ForeignFetchResponse.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
+#include "public/platform/WebCORS.h"
 
 namespace blink {
 
@@ -68,16 +68,16 @@
         kWebServiceWorkerResponseErrorForeignFetchMismatchedOrigin);
     return;
   } else if (!is_opaque) {
-    HTTPHeaderSet headers;
+    WebCORS::HTTPHeaderSet headers;
     if (foreign_fetch_response.hasHeaders()) {
       for (const String& header : foreign_fetch_response.headers())
         headers.insert(header);
       if (response->GetResponse()->GetType() == FetchResponseData::kCORSType) {
-        const HTTPHeaderSet& existing_headers =
+        const WebCORS::HTTPHeaderSet& existing_headers =
             response->GetResponse()->CorsExposedHeaderNames();
-        HTTPHeaderSet headers_to_remove;
-        for (HTTPHeaderSet::iterator it = headers.begin(); it != headers.end();
-             ++it) {
+        WebCORS::HTTPHeaderSet headers_to_remove;
+        for (WebCORS::HTTPHeaderSet::iterator it = headers.begin();
+             it != headers.end(); ++it) {
           if (!existing_headers.Contains(*it))
             headers_to_remove.insert(*it);
         }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 58d43cb..b44915a5 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -565,6 +565,7 @@
     "exported/WebAudioBus.cpp",
     "exported/WebAudioDevice.cpp",
     "exported/WebBlobData.cpp",
+    "exported/WebCORS.cpp",
     "exported/WebCache.cpp",
     "exported/WebCanvasCaptureHandler.cpp",
     "exported/WebCoalescedInputEvent.cpp",
@@ -1743,6 +1744,7 @@
     "blob/BlobBytesProviderTest.cpp",
     "blob/BlobDataTest.cpp",
     "exported/FilePathConversionTest.cpp",
+    "exported/WebCORSTest.cpp",
     "exported/WebStringTest.cpp",
     "feature_policy/FeaturePolicyTest.cpp",
     "fonts/AcceptLanguagesResolverTest.cpp",
@@ -2140,6 +2142,7 @@
     "graphics/ImageLayerChromiumTest.cpp",
     "graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp",
     "graphics/test/FakeGLES2Interface.h",
+    "graphics/test/FakeScrollableArea.h",
     "graphics/test/FakeWebGraphicsContext3DProvider.h",
     "graphics/test/MockImageDecoder.h",
     "graphics/test/MockPaintCanvas.h",
diff --git a/third_party/WebKit/Source/platform/DEPS b/third_party/WebKit/Source/platform/DEPS
index e935b5e5..02ad5d36 100644
--- a/third_party/WebKit/Source/platform/DEPS
+++ b/third_party/WebKit/Source/platform/DEPS
@@ -33,6 +33,7 @@
     "+base/timer",
     "+base/trace_event",
     "+base/values.h",
+    "+net/http/http_util.h",
     "+device",
     "+gpu/GLES2",
     "+mojo/public",
diff --git a/third_party/WebKit/Source/platform/exported/WebCORS.cpp b/third_party/WebKit/Source/platform/exported/WebCORS.cpp
new file mode 100644
index 0000000..d7c0cf1a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/exported/WebCORS.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "public/platform/WebCORS.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "net/http/http_util.h"
+#include "platform/HTTPNames.h"
+#include "platform/loader/fetch/FetchUtils.h"
+#include "platform/loader/fetch/ResourceRequest.h"
+#include "platform/network/HTTPHeaderMap.h"
+#include "platform/weborigin/KURL.h"
+#include "platform/weborigin/SchemeRegistry.h"
+#include "platform/wtf/text/StringBuilder.h"
+#include "platform/wtf/text/WTFString.h"
+#include "public/platform/WebSecurityOrigin.h"
+#include "public/platform/WebURLResponse.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+namespace WebCORS {
+
+namespace {
+
+bool IsInterestingStatusCode(int status_code) {
+  // Predicate that gates what status codes should be included in console error
+  // messages for responses containing no access control headers.
+  return status_code >= 400;
+}
+
+// Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch-0
+String CreateAccessControlRequestHeadersHeader(const HTTPHeaderMap& headers) {
+  Vector<String> filtered_headers;
+  for (const auto& header : headers) {
+    if (FetchUtils::IsCORSSafelistedHeader(header.key, header.value)) {
+      // Exclude CORS-safelisted headers.
+      continue;
+    }
+    // TODO(hintzed) replace with EqualIgnoringASCIICase()
+    if (DeprecatedEqualIgnoringCase(header.key, "referer")) {
+      // When the request is from a Worker, referrer header was added by
+      // WorkerThreadableLoader. But it should not be added to
+      // Access-Control-Request-Headers header.
+      continue;
+    }
+    filtered_headers.push_back(header.key.DeprecatedLower());
+  }
+  if (!filtered_headers.size())
+    return g_null_atom;
+
+  // Sort header names lexicographically.
+  std::sort(filtered_headers.begin(), filtered_headers.end(),
+            WTF::CodePointCompareLessThan);
+  StringBuilder header_buffer;
+  for (const String& header : filtered_headers) {
+    if (!header_buffer.IsEmpty())
+      header_buffer.Append(",");
+    header_buffer.Append(header);
+  }
+
+  return header_buffer.ToString();
+}
+
+// A parser for the value of the Access-Control-Expose-Headers header.
+class HTTPHeaderNameListParser {
+  STACK_ALLOCATED();
+
+ public:
+  explicit HTTPHeaderNameListParser(const String& value)
+      : value_(value), pos_(0) {}
+
+  // Tries parsing |value_| expecting it to be conforming to the #field-name
+  // ABNF rule defined in RFC 7230. Returns with the field-name entries stored
+  // in |output| when successful. Otherwise, returns with |output| kept empty.
+  //
+  // |output| must be empty.
+  void Parse(HTTPHeaderSet& output) {
+    DCHECK(output.IsEmpty());
+
+    while (true) {
+      ConsumeSpaces();
+
+      size_t token_start = pos_;
+      ConsumeTokenChars();
+      size_t token_size = pos_ - token_start;
+      if (token_size == 0) {
+        output.clear();
+        return;
+      }
+      output.insert(value_.Substring(token_start, token_size));
+
+      ConsumeSpaces();
+
+      if (pos_ == value_.length()) {
+        return;
+      }
+
+      if (value_[pos_] == ',') {
+        ++pos_;
+      } else {
+        output.clear();
+        return;
+      }
+    }
+  }
+
+ private:
+  // Consumes zero or more spaces (SP and HTAB) from value_.
+  void ConsumeSpaces() {
+    while (true) {
+      if (pos_ == value_.length()) {
+        return;
+      }
+
+      UChar c = value_[pos_];
+      if (c != ' ' && c != '\t') {
+        return;
+      }
+      ++pos_;
+    }
+  }
+
+  // Consumes zero or more tchars from value_.
+  void ConsumeTokenChars() {
+    while (true) {
+      if (pos_ == value_.length()) {
+        return;
+      }
+
+      UChar c = value_[pos_];
+      if (c > 0x7F || !net::HttpUtil::IsTokenChar(c)) {
+        return;
+      }
+      ++pos_;
+    }
+  }
+
+  const String value_;
+  size_t pos_;
+};
+
+static bool IsOriginSeparator(UChar ch) {
+  return IsASCIISpace(ch) || ch == ',';
+}
+
+}  // namespace
+
+AccessStatus CheckAccess(
+    const WebURLResponse& response,
+    const WebURLRequest::FetchCredentialsMode credentials_mode,
+    const WebSecurityOrigin& security_origin) {
+  int status_code = response.HttpStatusCode();
+  if (!status_code)
+    return AccessStatus::kInvalidResponse;
+
+  const WebString& allow_origin_header_value =
+      response.HttpHeaderField(HTTPNames::Access_Control_Allow_Origin);
+
+  // Check Suborigins, unless the Access-Control-Allow-Origin is '*', which
+  // implies that all Suborigins are okay as well.
+  if (!security_origin.Suborigin().IsEmpty() &&
+      allow_origin_header_value != WebString(g_star_atom)) {
+    const WebString& allow_suborigin_header_value =
+        response.HttpHeaderField(HTTPNames::Access_Control_Allow_Suborigin);
+    if (allow_suborigin_header_value != WebString(g_star_atom) &&
+        allow_suborigin_header_value != security_origin.Suborigin()) {
+      return AccessStatus::kSubOriginMismatch;
+    }
+  }
+
+  if (allow_origin_header_value == "*") {
+    // A wildcard Access-Control-Allow-Origin can not be used if credentials are
+    // to be sent, even with Access-Control-Allow-Credentials set to true.
+    if (!FetchUtils::ShouldTreatCredentialsModeAsInclude(credentials_mode))
+      return AccessStatus::kAccessAllowed;
+    // TODO(hintzed): Is the following a sound substitute for
+    // blink::ResourceResponse::IsHTTP()?
+    if (GURL(response.Url().GetString().Utf16()).SchemeIsHTTPOrHTTPS()) {
+      return AccessStatus::kWildcardOriginNotAllowed;
+    }
+  } else if (allow_origin_header_value != security_origin.ToString()) {
+    if (allow_origin_header_value.IsNull())
+      return AccessStatus::kMissingAllowOriginHeader;
+    if (String(allow_origin_header_value).Find(IsOriginSeparator, 0) !=
+        kNotFound) {
+      return AccessStatus::kMultipleAllowOriginValues;
+    }
+    KURL header_origin(NullURL(), allow_origin_header_value);
+    if (!header_origin.IsValid())
+      return AccessStatus::kInvalidAllowOriginValue;
+
+    return AccessStatus::kAllowOriginMismatch;
+  }
+
+  if (FetchUtils::ShouldTreatCredentialsModeAsInclude(credentials_mode)) {
+    const WebString& allow_credentials_header_value =
+        response.HttpHeaderField(HTTPNames::Access_Control_Allow_Credentials);
+    if (allow_credentials_header_value != "true") {
+      return AccessStatus::kDisallowCredentialsNotSetToTrue;
+    }
+  }
+  return AccessStatus::kAccessAllowed;
+}
+
+bool HandleRedirect(WebSecurityOrigin& current_security_origin,
+                    WebURLRequest& new_request,
+                    const WebURLResponse& redirect_response,
+                    WebURLRequest::FetchCredentialsMode credentials_mode,
+                    ResourceLoaderOptions& options,
+                    WebString& error_message) {
+  const KURL& last_url = redirect_response.Url();
+  const KURL& new_url = new_request.Url();
+
+  WebSecurityOrigin& new_security_origin = current_security_origin;
+
+  // TODO(tyoshino): This should be fixed to check not only the last one but
+  // all redirect responses.
+  if (!current_security_origin.CanRequest(last_url)) {
+    RedirectStatus redirect_status = CheckRedirectLocation(new_url);
+    if (redirect_status != RedirectStatus::kRedirectSuccess) {
+      StringBuilder builder;
+      builder.Append("Redirect from '");
+      builder.Append(last_url.GetString());
+      builder.Append("' has been blocked by CORS policy: ");
+      builder.Append(RedirectErrorString(redirect_status, new_url));
+      error_message = builder.ToString();
+      return false;
+    }
+
+    AccessStatus cors_status = CheckAccess(redirect_response, credentials_mode,
+                                           current_security_origin);
+    if (cors_status != AccessStatus::kAccessAllowed) {
+      StringBuilder builder;
+      builder.Append("Redirect from '");
+      builder.Append(last_url.GetString());
+      builder.Append("' has been blocked by CORS policy: ");
+      builder.Append(AccessControlErrorString(
+          cors_status, redirect_response,
+          WebSecurityOrigin(current_security_origin.Get()),
+          new_request.GetRequestContext()));
+      error_message = builder.ToString();
+      return false;
+    }
+
+    RefPtr<SecurityOrigin> last_origin = SecurityOrigin::Create(last_url);
+    // Set request's origin to a globally unique identifier as specified in
+    // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch.
+    if (!last_origin->CanRequest(new_url)) {
+      options.security_origin = SecurityOrigin::CreateUnique();
+      new_security_origin = options.security_origin;
+    }
+  }
+
+  if (!current_security_origin.CanRequest(new_url)) {
+    new_request.ClearHTTPHeaderField(WebString(HTTPNames::Suborigin));
+    new_request.SetHTTPHeaderField(WebString(HTTPNames::Origin),
+                                   new_security_origin.ToString());
+    if (!new_security_origin.Suborigin().IsEmpty()) {
+      new_request.SetHTTPHeaderField(WebString(HTTPNames::Suborigin),
+                                     new_security_origin.Suborigin());
+    }
+
+    options.cors_flag = true;
+  }
+  return true;
+}
+
+RedirectStatus CheckRedirectLocation(const WebURL& web_request_url) {
+  // Block non HTTP(S) schemes as specified in the step 4 in
+  // https://fetch.spec.whatwg.org/#http-redirect-fetch. Chromium also allows
+  // the data scheme.
+  //
+  // TODO(tyoshino): This check should be performed regardless of the CORS flag
+  // and request's mode.
+  KURL request_url = web_request_url;
+
+  if (!SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(
+          request_url.Protocol()))
+    return RedirectStatus::kRedirectDisallowedScheme;
+
+  // Block URLs including credentials as specified in the step 9 in
+  // https://fetch.spec.whatwg.org/#http-redirect-fetch.
+  //
+  // TODO(tyoshino): This check should be performed also when request's
+  // origin is not same origin with the redirect destination's origin.
+  if (!(request_url.User().IsEmpty() && request_url.Pass().IsEmpty()))
+    return RedirectStatus::kRedirectContainsCredentials;
+
+  return RedirectStatus::kRedirectSuccess;
+}
+
+PreflightStatus CheckPreflight(const WebURLResponse& response) {
+  // CORS preflight with 3XX is considered network error in
+  // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch
+  // CORS Spec: http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
+  // https://crbug.com/452394
+  int status_code = response.HttpStatusCode();
+  if (!FetchUtils::IsOkStatus(status_code))
+    return PreflightStatus::kPreflightInvalidStatus;
+
+  return PreflightStatus::kPreflightSuccess;
+}
+
+PreflightStatus CheckExternalPreflight(const WebURLResponse& response) {
+  WebString result =
+      response.HttpHeaderField(HTTPNames::Access_Control_Allow_External);
+  if (result.IsNull())
+    return PreflightStatus::kPreflightMissingAllowExternal;
+  // TODO(hintzed) replace with EqualIgnoringASCIICase()
+  if (!DeprecatedEqualIgnoringCase(result, "true"))
+    return PreflightStatus::kPreflightInvalidAllowExternal;
+  return PreflightStatus::kPreflightSuccess;
+}
+
+WebURLRequest CreateAccessControlPreflightRequest(
+    const WebURLRequest& request) {
+  const KURL& request_url = request.Url();
+
+  DCHECK(request_url.User().IsEmpty());
+  DCHECK(request_url.Pass().IsEmpty());
+
+  WebURLRequest preflight_request(request_url);
+  preflight_request.SetHTTPMethod(HTTPNames::OPTIONS);
+  preflight_request.SetHTTPHeaderField(HTTPNames::Access_Control_Request_Method,
+                                       request.HttpMethod());
+  preflight_request.SetPriority(request.GetPriority());
+  preflight_request.SetRequestContext(request.GetRequestContext());
+  preflight_request.SetFetchCredentialsMode(
+      WebURLRequest::kFetchCredentialsModeOmit);
+  preflight_request.SetServiceWorkerMode(
+      WebURLRequest::ServiceWorkerMode::kNone);
+
+  if (request.IsExternalRequest()) {
+    preflight_request.SetHTTPHeaderField(
+        HTTPNames::Access_Control_Request_External, "true");
+  }
+
+  String request_headers = CreateAccessControlRequestHeadersHeader(
+      request.ToResourceRequest().HttpHeaderFields());
+  if (request_headers != g_null_atom) {
+    preflight_request.SetHTTPHeaderField(
+        HTTPNames::Access_Control_Request_Headers, request_headers);
+  }
+
+  return preflight_request;
+}
+
+WebString AccessControlErrorString(
+    const AccessStatus status,
+    const WebURLResponse& response,
+    const WebSecurityOrigin& origin,
+    const WebURLRequest::RequestContext context) {
+  String origin_denied =
+      String::Format("Origin '%s' is therefore not allowed access.",
+                     origin.ToString().Utf8().data());
+
+  String no_cors_information =
+      context == WebURLRequest::kRequestContextFetch
+          ? " Have the server send the header with a valid value, or, if an "
+            "opaque response serves your needs, set the request's mode to "
+            "'no-cors' to fetch the resource with CORS disabled."
+          : "";
+
+  switch (status) {
+    case AccessStatus::kInvalidResponse: {
+      return String::Format("Invalid response. %s",
+                            origin_denied.Utf8().data());
+    }
+    case AccessStatus::kSubOriginMismatch: {
+      return String::Format(
+          "The 'Access-Control-Allow-Suborigin' header has a value '%s' that "
+          "is not equal to the supplied suborigin. %s",
+          response.HttpHeaderField(HTTPNames::Access_Control_Allow_Suborigin)
+              .Utf8()
+              .data(),
+          origin_denied.Utf8().data());
+    }
+    case AccessStatus::kWildcardOriginNotAllowed: {
+      return String::Format(
+          "The value of the 'Access-Control-Allow-Origin' header in the "
+          "response must not be the wildcard '*' when the request's "
+          "credentials mode is 'include'. %s%s",
+          origin_denied.Utf8().data(),
+          context == WebURLRequest::kRequestContextXMLHttpRequest
+              ? " The credentials mode of requests initiated by the "
+                "XMLHttpRequest is controlled by the withCredentials attribute."
+              : "");
+    }
+    case AccessStatus::kMissingAllowOriginHeader: {
+      String status_code_msg =
+          IsInterestingStatusCode(response.HttpStatusCode())
+              ? String::Format(" The response had HTTP status code %d.",
+                               response.HttpStatusCode())
+              : "";
+
+      return String::Format(
+          "No 'Access-Control-Allow-Origin' header is present on the "
+          "requested resource. %s%s%s",
+          origin_denied.Utf8().data(), status_code_msg.Utf8().data(),
+          context == WebURLRequest::kRequestContextFetch
+              ? " If an opaque response serves your needs, set the request's "
+                "mode to 'no-cors' to fetch the resource with CORS disabled."
+              : "");
+    }
+    case AccessStatus::kMultipleAllowOriginValues: {
+      return String::Format(
+          "The 'Access-Control-Allow-Origin' header contains multiple values "
+          "'%s', but only one is allowed. %s%s",
+          response.HttpHeaderField(HTTPNames::Access_Control_Allow_Origin)
+              .Utf8()
+              .data(),
+          origin_denied.Utf8().data(), no_cors_information.Utf8().data());
+    }
+    case AccessStatus::kInvalidAllowOriginValue: {
+      return String::Format(
+          "The 'Access-Control-Allow-Origin' header contains the invalid value "
+          "'%s'. %s%s",
+          response.HttpHeaderField(HTTPNames::Access_Control_Allow_Origin)
+              .Utf8()
+              .data(),
+          origin_denied.Utf8().data(), no_cors_information.Utf8().data());
+    }
+    case AccessStatus::kAllowOriginMismatch: {
+      return String::Format(
+          "The 'Access-Control-Allow-Origin' header has a value '%s' that is "
+          "not equal to the supplied origin. %s%s",
+          response.HttpHeaderField(HTTPNames::Access_Control_Allow_Origin)
+              .Utf8()
+              .data(),
+          origin_denied.Utf8().data(), no_cors_information.Utf8().data());
+    }
+    case AccessStatus::kDisallowCredentialsNotSetToTrue: {
+      return String::Format(
+          "The value of the 'Access-Control-Allow-Credentials' header in "
+          "the response is '%s' which must be 'true' when the request's "
+          "credentials mode is 'include'. %s%s",
+          response.HttpHeaderField(HTTPNames::Access_Control_Allow_Credentials)
+              .Utf8()
+              .data(),
+          origin_denied.Utf8().data(),
+          (context == WebURLRequest::kRequestContextXMLHttpRequest
+               ? " The credentials mode of requests initiated by the "
+                 "XMLHttpRequest is controlled by the withCredentials "
+                 "attribute."
+               : ""));
+    }
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+WebString PreflightErrorString(PreflightStatus status,
+                               const WebURLResponse& response) {
+  switch (status) {
+    case PreflightStatus::kPreflightInvalidStatus: {
+      return String::Format(
+          "Response for preflight has invalid HTTP status code %d",
+          response.HttpStatusCode());
+    }
+    case PreflightStatus::kPreflightMissingAllowExternal: {
+      return String::Format(
+          "No 'Access-Control-Allow-External' header was present in the "
+          "preflight response for this external request (This is an "
+          "experimental header which is defined in "
+          "'https://wicg.github.io/cors-rfc1918/').");
+    }
+    case PreflightStatus::kPreflightInvalidAllowExternal: {
+      return String::Format(
+          "The 'Access-Control-Allow-External' header in the preflight "
+          "response for this external request had a value of '%s',  not 'true' "
+          "(This is an experimental header which is defined in "
+          "'https://wicg.github.io/cors-rfc1918/').",
+          response.HttpHeaderField("access-control-allow-external")
+              .Utf8()
+              .data());
+    }
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+WebString RedirectErrorString(const RedirectStatus status,
+                              const WebURL& redirect_url) {
+  switch (status) {
+    case kRedirectDisallowedScheme: {
+      return String::Format(
+          "Redirect location '%s' has a disallowed scheme for cross-origin "
+          "requests.",
+          redirect_url.GetString().Utf8().data());
+    }
+    case kRedirectContainsCredentials: {
+      return String::Format(
+          "Redirect location '%s' contains a username and password, which is "
+          "disallowed for cross-origin requests.",
+          redirect_url.GetString().Utf8().data());
+    }
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+void ExtractCorsExposedHeaderNamesList(const WebURLResponse& response,
+                                       HTTPHeaderSet& header_set) {
+  // If a response was fetched via a service worker, it will always have
+  // CorsExposedHeaderNames set, either from the Access-Control-Expose-Headers
+  // header, or explicitly via foreign fetch. For requests that didn't come from
+  // a service worker, foreign fetch doesn't apply so just parse the CORS
+  // header.
+  if (response.WasFetchedViaServiceWorker()) {
+    for (const auto& header : response.CorsExposedHeaderNames())
+      header_set.insert(String(header));
+    return;
+  }
+  ParseAccessControlExposeHeadersAllowList(
+      response.HttpHeaderField(
+          WebString(HTTPNames::Access_Control_Expose_Headers)),
+      header_set);
+}
+
+void ParseAccessControlExposeHeadersAllowList(const WebString& header_value,
+                                              HTTPHeaderSet& header_set) {
+  HTTPHeaderNameListParser parser(header_value);
+  parser.Parse(header_set);
+}
+
+bool IsOnAccessControlResponseHeaderWhitelist(const WebString& name) {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      HTTPHeaderSet, allowed_cross_origin_response_headers,
+      ({
+          "cache-control", "content-language", "content-type", "expires",
+          "last-modified", "pragma",
+      }));
+  return allowed_cross_origin_response_headers.Contains(name);
+}
+
+}  // namespace WebCORS
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/exported/WebCORSTest.cpp b/third_party/WebKit/Source/platform/exported/WebCORSTest.cpp
new file mode 100644
index 0000000..d14118d
--- /dev/null
+++ b/third_party/WebKit/Source/platform/exported/WebCORSTest.cpp
@@ -0,0 +1,167 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/platform/WebCORS.h"
+
+#include "platform/loader/fetch/ResourceRequest.h"
+#include "platform/weborigin/SecurityOrigin.h"
+#include "platform/wtf/RefPtr.h"
+#include "platform/wtf/text/WTFString.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+TEST(CreateAccessControlPreflightRequestTest, LexicographicalOrder) {
+  WebURLRequest request;
+  request.AddHTTPHeaderField("Orange", "Orange");
+  request.AddHTTPHeaderField("Apple", "Red");
+  request.AddHTTPHeaderField("Kiwifruit", "Green");
+  request.AddHTTPHeaderField("Content-Type", "application/octet-stream");
+  request.AddHTTPHeaderField("Strawberry", "Red");
+
+  WebURLRequest preflight =
+      WebCORS::CreateAccessControlPreflightRequest(request);
+
+  EXPECT_EQ("apple,content-type,kiwifruit,orange,strawberry",
+            preflight.HttpHeaderField("Access-Control-Request-Headers"));
+}
+
+TEST(CreateAccessControlPreflightRequestTest, ExcludeSimpleHeaders) {
+  WebURLRequest request;
+  request.AddHTTPHeaderField("Accept", "everything");
+  request.AddHTTPHeaderField("Accept-Language", "everything");
+  request.AddHTTPHeaderField("Content-Language", "everything");
+  request.AddHTTPHeaderField("Save-Data", "on");
+
+  WebURLRequest preflight =
+      WebCORS::CreateAccessControlPreflightRequest(request);
+
+  // Do not emit empty-valued headers; an empty list of non-"CORS safelisted"
+  // request headers should cause "Access-Control-Request-Headers:" to be
+  // left out in the preflight request.
+  EXPECT_EQ(WebString(g_null_atom),
+            preflight.HttpHeaderField("Access-Control-Request-Headers"));
+}
+
+TEST(CreateAccessControlPreflightRequestTest, ExcludeSimpleContentTypeHeader) {
+  WebURLRequest request;
+  request.AddHTTPHeaderField("Content-Type", "text/plain");
+
+  WebURLRequest preflight =
+      WebCORS::CreateAccessControlPreflightRequest(request);
+
+  // Empty list also; see comment in test above.
+  EXPECT_EQ(WebString(g_null_atom),
+            preflight.HttpHeaderField("Access-Control-Request-Headers"));
+}
+
+TEST(CreateAccessControlPreflightRequestTest, IncludeNonSimpleHeader) {
+  WebURLRequest request;
+  request.AddHTTPHeaderField("X-Custom-Header", "foobar");
+
+  WebURLRequest preflight =
+      WebCORS::CreateAccessControlPreflightRequest(request);
+
+  EXPECT_EQ("x-custom-header",
+            preflight.HttpHeaderField("Access-Control-Request-Headers"));
+}
+
+TEST(CreateAccessControlPreflightRequestTest,
+     IncludeNonSimpleContentTypeHeader) {
+  WebURLRequest request;
+  request.AddHTTPHeaderField("Content-Type", "application/octet-stream");
+
+  WebURLRequest preflight =
+      WebCORS::CreateAccessControlPreflightRequest(request);
+
+  EXPECT_EQ("content-type",
+            preflight.HttpHeaderField("Access-Control-Request-Headers"));
+}
+
+TEST(ParseAccessControlExposeHeadersAllowListTest, ValidInput) {
+  WebCORS::HTTPHeaderSet set;
+  WebCORS::ParseAccessControlExposeHeadersAllowList("valid", set);
+  EXPECT_EQ(1U, set.size());
+  EXPECT_TRUE(set.Contains("valid"));
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("a,b", set);
+  EXPECT_EQ(2U, set.size());
+  EXPECT_TRUE(set.Contains("a"));
+  EXPECT_TRUE(set.Contains("b"));
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("   a ,  b ", set);
+  EXPECT_EQ(2U, set.size());
+  EXPECT_TRUE(set.Contains("a"));
+  EXPECT_TRUE(set.Contains("b"));
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList(" \t   \t\t a", set);
+  EXPECT_EQ(1U, set.size());
+  EXPECT_TRUE(set.Contains("a"));
+}
+
+TEST(ParseAccessControlExposeHeadersAllowListTest, DuplicatedEntries) {
+  WebCORS::HTTPHeaderSet set;
+  WebCORS::ParseAccessControlExposeHeadersAllowList("a, a", set);
+  EXPECT_EQ(1U, set.size());
+  EXPECT_TRUE(set.Contains("a"));
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("a, a, b", set);
+  EXPECT_EQ(2U, set.size());
+  EXPECT_TRUE(set.Contains("a"));
+  EXPECT_TRUE(set.Contains("b"));
+}
+
+TEST(ParseAccessControlExposeHeadersAllowListTest, InvalidInput) {
+  WebCORS::HTTPHeaderSet set;
+  WebCORS::ParseAccessControlExposeHeadersAllowList("not valid", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("///", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("/a/", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList(",", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList(" , ", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList(" , a", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("a , ", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList("", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  WebCORS::ParseAccessControlExposeHeadersAllowList(" ", set);
+  EXPECT_TRUE(set.IsEmpty());
+
+  set.clear();
+  // U+0141 which is 'A' (0x41) + 0x100.
+  WebCORS::ParseAccessControlExposeHeadersAllowList(
+      String::FromUTF8("\xC5\x81"), set);
+  EXPECT_TRUE(set.IsEmpty());
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
index 7558c19..b37e16c 100644
--- a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
@@ -317,6 +317,10 @@
   resource_response_->SetCacheStorageCacheName(cache_storage_cache_name);
 }
 
+WebVector<WebString> WebURLResponse::CorsExposedHeaderNames() const {
+  return resource_response_->CorsExposedHeaderNames();
+}
+
 void WebURLResponse::SetCorsExposedHeaderNames(
     const WebVector<WebString>& header_names) {
   Vector<String> exposed_header_names;
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index ace06512..db0a8214 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -35,6 +35,7 @@
 #include "platform/animation/CompositorFloatAnimationCurve.h"
 #include "platform/animation/CompositorTargetProperty.h"
 #include "platform/graphics/CompositorElementId.h"
+#include "platform/graphics/test/FakeScrollableArea.h"
 #include "platform/scheduler/child/web_scheduler.h"
 #include "platform/scroll/ScrollableArea.h"
 #include "platform/testing/FakeGraphicsLayer.h"
@@ -166,53 +167,4 @@
   host.RemoveTimeline(*compositor_timeline.get());
 }
 
-class FakeScrollableArea : public GarbageCollectedFinalized<FakeScrollableArea>,
-                           public ScrollableArea {
-  USING_GARBAGE_COLLECTED_MIXIN(FakeScrollableArea);
-
- public:
-  static FakeScrollableArea* Create() { return new FakeScrollableArea; }
-
-  CompositorElementId GetCompositorElementId() const override {
-    return CompositorElementId();
-  }
-  bool IsActive() const override { return false; }
-  int ScrollSize(ScrollbarOrientation) const override { return 100; }
-  bool IsScrollCornerVisible() const override { return false; }
-  IntRect ScrollCornerRect() const override { return IntRect(); }
-  IntRect VisibleContentRect(
-      IncludeScrollbarsInRect = kExcludeScrollbars) const override {
-    return IntRect(ScrollOffsetInt().Width(), ScrollOffsetInt().Height(), 10,
-                   10);
-  }
-  IntSize ContentsSize() const override { return IntSize(100, 100); }
-  bool ScrollbarsCanBeActive() const override { return false; }
-  IntRect ScrollableAreaBoundingBox() const override { return IntRect(); }
-  void ScrollControlWasSetNeedsPaintInvalidation() override {}
-  bool UserInputScrollable(ScrollbarOrientation) const override { return true; }
-  bool ShouldPlaceVerticalScrollbarOnLeft() const override { return false; }
-  int PageStep(ScrollbarOrientation) const override { return 0; }
-  IntSize MinimumScrollOffsetInt() const override { return IntSize(); }
-  IntSize MaximumScrollOffsetInt() const override {
-    return ContentsSize() - IntSize(VisibleWidth(), VisibleHeight());
-  }
-
-  void UpdateScrollOffset(const ScrollOffset& offset, ScrollType) override {
-    scroll_offset_ = offset;
-  }
-  ScrollOffset GetScrollOffset() const override { return scroll_offset_; }
-  IntSize ScrollOffsetInt() const override {
-    return FlooredIntSize(scroll_offset_);
-  }
-
-  RefPtr<WebTaskRunner> GetTimerTaskRunner() const final {
-    return Platform::Current()->CurrentThread()->Scheduler()->TimerTaskRunner();
-  }
-
-  DEFINE_INLINE_VIRTUAL_TRACE() { ScrollableArea::Trace(visitor); }
-
- private:
-  ScrollOffset scroll_offset_;
-};
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/test/FakeScrollableArea.h b/third_party/WebKit/Source/platform/graphics/test/FakeScrollableArea.h
new file mode 100644
index 0000000..cf0a285d
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/test/FakeScrollableArea.h
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FakeScrollableArea_h
+#define FakeScrollableArea_h
+
+#include "platform/scheduler/child/web_scheduler.h"
+#include "platform/scroll/ScrollableArea.h"
+#include "public/platform/Platform.h"
+
+namespace blink {
+
+class FakeScrollableArea : public GarbageCollectedFinalized<FakeScrollableArea>,
+                           public ScrollableArea {
+  USING_GARBAGE_COLLECTED_MIXIN(FakeScrollableArea);
+
+ public:
+  static FakeScrollableArea* Create() { return new FakeScrollableArea; }
+
+  CompositorElementId GetCompositorElementId() const override {
+    return CompositorElementId();
+  }
+  bool IsActive() const override { return false; }
+  int ScrollSize(ScrollbarOrientation) const override { return 100; }
+  bool IsScrollCornerVisible() const override { return false; }
+  IntRect ScrollCornerRect() const override { return IntRect(); }
+  IntRect VisibleContentRect(
+      IncludeScrollbarsInRect = kExcludeScrollbars) const override {
+    return IntRect(ScrollOffsetInt().Width(), ScrollOffsetInt().Height(), 10,
+                   10);
+  }
+  IntSize ContentsSize() const override { return IntSize(100, 100); }
+  bool ScrollbarsCanBeActive() const override { return false; }
+  IntRect ScrollableAreaBoundingBox() const override { return IntRect(); }
+  void ScrollControlWasSetNeedsPaintInvalidation() override {}
+  bool UserInputScrollable(ScrollbarOrientation) const override { return true; }
+  bool ShouldPlaceVerticalScrollbarOnLeft() const override { return false; }
+  int PageStep(ScrollbarOrientation) const override { return 0; }
+  IntSize MinimumScrollOffsetInt() const override { return IntSize(); }
+  IntSize MaximumScrollOffsetInt() const override {
+    return ContentsSize() - IntSize(VisibleWidth(), VisibleHeight());
+  }
+
+  void UpdateScrollOffset(const ScrollOffset& offset, ScrollType) override {
+    scroll_offset_ = offset;
+  }
+  ScrollOffset GetScrollOffset() const override { return scroll_offset_; }
+  IntSize ScrollOffsetInt() const override {
+    return FlooredIntSize(scroll_offset_);
+  }
+
+  RefPtr<WebTaskRunner> GetTimerTaskRunner() const final {
+    return Platform::Current()->CurrentThread()->Scheduler()->TimerTaskRunner();
+  }
+
+  DEFINE_INLINE_VIRTUAL_TRACE() { ScrollableArea::Trace(visitor); }
+
+ private:
+  ScrollOffset scroll_offset_;
+};
+
+}  // namespace blink
+
+#endif  // FakeScrollableArea
diff --git a/third_party/WebKit/Source/platform/loader/BUILD.gn b/third_party/WebKit/Source/platform/loader/BUILD.gn
index 409befe..492cd20 100644
--- a/third_party/WebKit/Source/platform/loader/BUILD.gn
+++ b/third_party/WebKit/Source/platform/loader/BUILD.gn
@@ -31,8 +31,6 @@
     "fetch/CachedMetadataHandler.h",
     "fetch/ClientHintsPreferences.cpp",
     "fetch/ClientHintsPreferences.h",
-    "fetch/CrossOriginAccessControl.cpp",
-    "fetch/CrossOriginAccessControl.h",
     "fetch/FetchContext.cpp",
     "fetch/FetchContext.h",
     "fetch/FetchInitiatorInfo.h",
@@ -123,7 +121,6 @@
     "SubresourceIntegrityTest.cpp",
     "fetch/BufferingDataPipeWriterTest.cpp",
     "fetch/ClientHintsPreferencesTest.cpp",
-    "fetch/CrossOriginAccessControlTest.cpp",
     "fetch/FetchUtilsTest.cpp",
     "fetch/MemoryCacheCorrectnessTest.cpp",
     "fetch/MemoryCacheTest.cpp",
diff --git a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.cpp b/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.cpp
deleted file mode 100644
index 8a1246b..0000000
--- a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.cpp
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
-
-#include <algorithm>
-#include <memory>
-#include "net/http/http_util.h"
-#include "platform/loader/fetch/FetchUtils.h"
-#include "platform/loader/fetch/Resource.h"
-#include "platform/loader/fetch/ResourceLoaderOptions.h"
-#include "platform/loader/fetch/ResourceRequest.h"
-#include "platform/loader/fetch/ResourceResponse.h"
-#include "platform/network/HTTPParsers.h"
-#include "platform/weborigin/SchemeRegistry.h"
-#include "platform/weborigin/SecurityOrigin.h"
-#include "platform/wtf/PtrUtil.h"
-#include "platform/wtf/Threading.h"
-#include "platform/wtf/text/AtomicString.h"
-#include "platform/wtf/text/StringBuilder.h"
-
-namespace blink {
-
-bool CrossOriginAccessControl::IsOnAccessControlResponseHeaderWhitelist(
-    const String& name) {
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(
-      HTTPHeaderSet, allowed_cross_origin_response_headers,
-      ({
-          "cache-control", "content-language", "content-type", "expires",
-          "last-modified", "pragma",
-      }));
-  return allowed_cross_origin_response_headers.Contains(name);
-}
-
-// Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch-0
-static AtomicString CreateAccessControlRequestHeadersHeader(
-    const HTTPHeaderMap& headers) {
-  Vector<String> filtered_headers;
-  for (const auto& header : headers) {
-    if (FetchUtils::IsCORSSafelistedHeader(header.key, header.value)) {
-      // Exclude CORS-safelisted headers.
-      continue;
-    }
-    if (DeprecatedEqualIgnoringCase(header.key, "referer")) {
-      // When the request is from a Worker, referrer header was added by
-      // WorkerThreadableLoader. But it should not be added to
-      // Access-Control-Request-Headers header.
-      continue;
-    }
-    filtered_headers.push_back(header.key.DeprecatedLower());
-  }
-  if (!filtered_headers.size())
-    return g_null_atom;
-
-  // Sort header names lexicographically.
-  std::sort(filtered_headers.begin(), filtered_headers.end(),
-            WTF::CodePointCompareLessThan);
-  StringBuilder header_buffer;
-  for (const String& header : filtered_headers) {
-    if (!header_buffer.IsEmpty())
-      header_buffer.Append(",");
-    header_buffer.Append(header);
-  }
-
-  return AtomicString(header_buffer.ToString());
-}
-
-ResourceRequest CrossOriginAccessControl::CreateAccessControlPreflightRequest(
-    const ResourceRequest& request) {
-  const KURL& request_url = request.Url();
-
-  DCHECK(request_url.User().IsEmpty());
-  DCHECK(request_url.Pass().IsEmpty());
-
-  ResourceRequest preflight_request(request_url);
-  preflight_request.SetHTTPMethod(HTTPNames::OPTIONS);
-  preflight_request.SetHTTPHeaderField(HTTPNames::Access_Control_Request_Method,
-                                       AtomicString(request.HttpMethod()));
-  preflight_request.SetPriority(request.Priority());
-  preflight_request.SetRequestContext(request.GetRequestContext());
-  preflight_request.SetFetchCredentialsMode(
-      WebURLRequest::kFetchCredentialsModeOmit);
-  preflight_request.SetServiceWorkerMode(
-      WebURLRequest::ServiceWorkerMode::kNone);
-
-  if (request.IsExternalRequest()) {
-    preflight_request.SetHTTPHeaderField(
-        HTTPNames::Access_Control_Request_External, "true");
-  }
-
-  AtomicString request_headers =
-      CreateAccessControlRequestHeadersHeader(request.HttpHeaderFields());
-  if (request_headers != g_null_atom) {
-    preflight_request.SetHTTPHeaderField(
-        HTTPNames::Access_Control_Request_Headers, request_headers);
-  }
-
-  return preflight_request;
-}
-
-static bool IsOriginSeparator(UChar ch) {
-  return IsASCIISpace(ch) || ch == ',';
-}
-
-CrossOriginAccessControl::AccessStatus CrossOriginAccessControl::CheckAccess(
-    const ResourceResponse& response,
-    WebURLRequest::FetchCredentialsMode credentials_mode,
-    const SecurityOrigin* security_origin) {
-  static const char allow_origin_header_name[] = "access-control-allow-origin";
-  static const char allow_credentials_header_name[] =
-      "access-control-allow-credentials";
-  static const char allow_suborigin_header_name[] =
-      "access-control-allow-suborigin";
-  int status_code = response.HttpStatusCode();
-  if (!status_code)
-    return kInvalidResponse;
-
-  const AtomicString& allow_origin_header_value =
-      response.HttpHeaderField(allow_origin_header_name);
-
-  // Check Suborigins, unless the Access-Control-Allow-Origin is '*', which
-  // implies that all Suborigins are okay as well.
-  if (security_origin->HasSuborigin() &&
-      allow_origin_header_value != g_star_atom) {
-    const AtomicString& allow_suborigin_header_value =
-        response.HttpHeaderField(allow_suborigin_header_name);
-    AtomicString atomic_suborigin_name(
-        security_origin->GetSuborigin()->GetName());
-    if (allow_suborigin_header_value != g_star_atom &&
-        allow_suborigin_header_value != atomic_suborigin_name) {
-      return kSubOriginMismatch;
-    }
-  }
-
-  if (allow_origin_header_value == "*") {
-    // A wildcard Access-Control-Allow-Origin can not be used if credentials are
-    // to be sent, even with Access-Control-Allow-Credentials set to true.
-    if (!FetchUtils::ShouldTreatCredentialsModeAsInclude(credentials_mode))
-      return kAccessAllowed;
-    if (response.IsHTTP()) {
-      return kWildcardOriginNotAllowed;
-    }
-  } else if (allow_origin_header_value != security_origin->ToAtomicString()) {
-    if (allow_origin_header_value.IsNull())
-      return kMissingAllowOriginHeader;
-    if (allow_origin_header_value.GetString().Find(IsOriginSeparator, 0) !=
-        kNotFound) {
-      return kMultipleAllowOriginValues;
-    }
-    KURL header_origin(NullURL(), allow_origin_header_value);
-    if (!header_origin.IsValid())
-      return kInvalidAllowOriginValue;
-
-    return kAllowOriginMismatch;
-  }
-
-  if (FetchUtils::ShouldTreatCredentialsModeAsInclude(credentials_mode)) {
-    const AtomicString& allow_credentials_header_value =
-        response.HttpHeaderField(allow_credentials_header_name);
-    if (allow_credentials_header_value != "true") {
-      return kDisallowCredentialsNotSetToTrue;
-    }
-  }
-  return kAccessAllowed;
-}
-
-static bool IsInterestingStatusCode(int status_code) {
-  // Predicate that gates what status codes should be included in console error
-  // messages for responses containing no access control headers.
-  return status_code >= 400;
-}
-
-static void AppendOriginDeniedMessage(StringBuilder& builder,
-                                      const SecurityOrigin* security_origin) {
-  builder.Append(" Origin '");
-  builder.Append(security_origin->ToString());
-  builder.Append("' is therefore not allowed access.");
-}
-
-static void AppendNoCORSInformationalMessage(
-    StringBuilder& builder,
-    WebURLRequest::RequestContext context) {
-  if (context != WebURLRequest::kRequestContextFetch)
-    return;
-  builder.Append(
-      " Have the server send the header with a valid value, or, if an "
-      "opaque response serves your needs, set the request's mode to "
-      "'no-cors' to fetch the resource with CORS disabled.");
-}
-
-void CrossOriginAccessControl::AccessControlErrorString(
-    StringBuilder& builder,
-    CrossOriginAccessControl::AccessStatus status,
-    const ResourceResponse& response,
-    const SecurityOrigin* security_origin,
-    WebURLRequest::RequestContext context) {
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allow_origin_header_name,
-                                  ("access-control-allow-origin"));
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allow_credentials_header_name,
-                                  ("access-control-allow-credentials"));
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allow_suborigin_header_name,
-                                  ("access-control-allow-suborigin"));
-
-  switch (status) {
-    case kInvalidResponse: {
-      builder.Append("Invalid response.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      return;
-    }
-    case kSubOriginMismatch: {
-      const AtomicString& allow_suborigin_header_value =
-          response.HttpHeaderField(allow_suborigin_header_name);
-      builder.Append(
-          "The 'Access-Control-Allow-Suborigin' header has a value '");
-      builder.Append(allow_suborigin_header_value);
-      builder.Append("' that is not equal to the supplied suborigin.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      return;
-    }
-    case kWildcardOriginNotAllowed: {
-      builder.Append(
-          "The value of the 'Access-Control-Allow-Origin' header in the "
-          "response must not be the wildcard '*' when the request's "
-          "credentials mode is 'include'.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      if (context == WebURLRequest::kRequestContextXMLHttpRequest) {
-        builder.Append(
-            " The credentials mode of requests initiated by the "
-            "XMLHttpRequest is controlled by the withCredentials attribute.");
-      }
-      return;
-    }
-    case kMissingAllowOriginHeader: {
-      builder.Append(
-          "No 'Access-Control-Allow-Origin' header is present on the requested "
-          "resource.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      int status_code = response.HttpStatusCode();
-      if (IsInterestingStatusCode(status_code)) {
-        builder.Append(" The response had HTTP status code ");
-        builder.Append(String::Number(status_code));
-        builder.Append('.');
-      }
-      if (context == WebURLRequest::kRequestContextFetch) {
-        builder.Append(
-            " If an opaque response serves your needs, set the request's mode "
-            "to 'no-cors' to fetch the resource with CORS disabled.");
-      }
-      return;
-    }
-    case kMultipleAllowOriginValues: {
-      const AtomicString& allow_origin_header_value =
-          response.HttpHeaderField(allow_origin_header_name);
-      builder.Append(
-          "The 'Access-Control-Allow-Origin' header contains multiple values "
-          "'");
-      builder.Append(allow_origin_header_value);
-      builder.Append("', but only one is allowed.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      AppendNoCORSInformationalMessage(builder, context);
-      return;
-    }
-    case kInvalidAllowOriginValue: {
-      const AtomicString& allow_origin_header_value =
-          response.HttpHeaderField(allow_origin_header_name);
-      builder.Append(
-          "The 'Access-Control-Allow-Origin' header contains the invalid "
-          "value '");
-      builder.Append(allow_origin_header_value);
-      builder.Append("'.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      AppendNoCORSInformationalMessage(builder, context);
-      return;
-    }
-    case kAllowOriginMismatch: {
-      const AtomicString& allow_origin_header_value =
-          response.HttpHeaderField(allow_origin_header_name);
-      builder.Append("The 'Access-Control-Allow-Origin' header has a value '");
-      builder.Append(allow_origin_header_value);
-      builder.Append("' that is not equal to the supplied origin.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      AppendNoCORSInformationalMessage(builder, context);
-      return;
-    }
-    case kDisallowCredentialsNotSetToTrue: {
-      const AtomicString& allow_credentials_header_value =
-          response.HttpHeaderField(allow_credentials_header_name);
-      builder.Append(
-          "The value of the 'Access-Control-Allow-Credentials' header in "
-          "the response is '");
-      builder.Append(allow_credentials_header_value);
-      builder.Append(
-          "' which must "
-          "be 'true' when the request's credentials mode is 'include'.");
-      AppendOriginDeniedMessage(builder, security_origin);
-      if (context == WebURLRequest::kRequestContextXMLHttpRequest) {
-        builder.Append(
-            " The credentials mode of requests initiated by the "
-            "XMLHttpRequest is controlled by the withCredentials attribute.");
-      }
-      return;
-    }
-    default:
-      NOTREACHED();
-  }
-}
-
-CrossOriginAccessControl::PreflightStatus
-CrossOriginAccessControl::CheckPreflight(const ResourceResponse& response) {
-  // CORS preflight with 3XX is considered network error in
-  // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch
-  // CORS Spec: http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
-  // https://crbug.com/452394
-  int status_code = response.HttpStatusCode();
-  if (!FetchUtils::IsOkStatus(status_code))
-    return kPreflightInvalidStatus;
-
-  return kPreflightSuccess;
-}
-
-CrossOriginAccessControl::PreflightStatus
-CrossOriginAccessControl::CheckExternalPreflight(
-    const ResourceResponse& response) {
-  AtomicString result =
-      response.HttpHeaderField(HTTPNames::Access_Control_Allow_External);
-  if (result.IsNull())
-    return kPreflightMissingAllowExternal;
-  if (!DeprecatedEqualIgnoringCase(result, "true"))
-    return kPreflightInvalidAllowExternal;
-  return kPreflightSuccess;
-}
-
-void CrossOriginAccessControl::PreflightErrorString(
-    StringBuilder& builder,
-    CrossOriginAccessControl::PreflightStatus status,
-    const ResourceResponse& response) {
-  switch (status) {
-    case kPreflightInvalidStatus: {
-      int status_code = response.HttpStatusCode();
-      builder.Append("Response for preflight has invalid HTTP status code ");
-      builder.Append(String::Number(status_code));
-      return;
-    }
-    case kPreflightMissingAllowExternal: {
-      builder.Append(
-          "No 'Access-Control-Allow-External' header was present in ");
-      builder.Append(
-          "the preflight response for this external request (This is");
-      builder.Append(" an experimental header which is defined in ");
-      builder.Append("'https://wicg.github.io/cors-rfc1918/').");
-      return;
-    }
-    case kPreflightInvalidAllowExternal: {
-      String result =
-          response.HttpHeaderField(HTTPNames::Access_Control_Allow_External);
-      builder.Append("The 'Access-Control-Allow-External' header in the ");
-      builder.Append(
-          "preflight response for this external request had a value");
-      builder.Append(" of '");
-      builder.Append(result);
-      builder.Append("',  not 'true' (This is an experimental header which is");
-      builder.Append(" defined in 'https://wicg.github.io/cors-rfc1918/').");
-      return;
-    }
-    default:
-      NOTREACHED();
-  }
-}
-
-// A parser for the value of the Access-Control-Expose-Headers header.
-class HTTPHeaderNameListParser {
-  STACK_ALLOCATED();
-
- public:
-  explicit HTTPHeaderNameListParser(const String& value)
-      : value_(value), pos_(0) {}
-
-  // Tries parsing |value_| expecting it to be conforming to the #field-name
-  // ABNF rule defined in RFC 7230. Returns with the field-name entries stored
-  // in |output| when successful. Otherwise, returns with |output| kept empty.
-  //
-  // |output| must be empty.
-  void Parse(HTTPHeaderSet& output) {
-    DCHECK(output.IsEmpty());
-
-    while (true) {
-      ConsumeSpaces();
-
-      size_t token_start = pos_;
-      ConsumeTokenChars();
-      size_t token_size = pos_ - token_start;
-      if (token_size == 0) {
-        output.clear();
-        return;
-      }
-      output.insert(value_.Substring(token_start, token_size));
-
-      ConsumeSpaces();
-
-      if (pos_ == value_.length()) {
-        return;
-      }
-
-      if (value_[pos_] == ',') {
-        ++pos_;
-      } else {
-        output.clear();
-        return;
-      }
-    }
-  }
-
- private:
-  // Consumes zero or more spaces (SP and HTAB) from value_.
-  void ConsumeSpaces() {
-    while (true) {
-      if (pos_ == value_.length()) {
-        return;
-      }
-
-      UChar c = value_[pos_];
-      if (c != ' ' && c != '\t') {
-        return;
-      }
-      ++pos_;
-    }
-  }
-
-  // Consumes zero or more tchars from value_.
-  void ConsumeTokenChars() {
-    while (true) {
-      if (pos_ == value_.length()) {
-        return;
-      }
-
-      UChar c = value_[pos_];
-      if (c > 0x7F || !net::HttpUtil::IsTokenChar(c)) {
-        return;
-      }
-      ++pos_;
-    }
-  }
-
-  const String& value_;
-  size_t pos_;
-};
-
-void CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-    const String& header_value,
-    HTTPHeaderSet& header_set) {
-  HTTPHeaderNameListParser parser(header_value);
-  parser.Parse(header_set);
-}
-
-void CrossOriginAccessControl::ExtractCorsExposedHeaderNamesList(
-    const ResourceResponse& response,
-    HTTPHeaderSet& header_set) {
-  // If a response was fetched via a service worker, it will always have
-  // corsExposedHeaderNames set, either from the Access-Control-Expose-Headers
-  // header, or explicitly via foreign fetch. For requests that didn't come from
-  // a service worker, foreign fetch doesn't apply so just parse the CORS
-  // header.
-  if (response.WasFetchedViaServiceWorker()) {
-    for (const auto& header : response.CorsExposedHeaderNames())
-      header_set.insert(header);
-    return;
-  }
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-      response.HttpHeaderField(HTTPNames::Access_Control_Expose_Headers),
-      header_set);
-}
-
-CrossOriginAccessControl::RedirectStatus
-CrossOriginAccessControl::CheckRedirectLocation(const KURL& request_url) {
-  // Block non HTTP(S) schemes as specified in the step 4 in
-  // https://fetch.spec.whatwg.org/#http-redirect-fetch. Chromium also allows
-  // the data scheme.
-  //
-  // TODO(tyoshino): This check should be performed regardless of the CORS flag
-  // and request's mode.
-  if (!SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(
-          request_url.Protocol()))
-    return kRedirectDisallowedScheme;
-
-  // Block URLs including credentials as specified in the step 9 in
-  // https://fetch.spec.whatwg.org/#http-redirect-fetch.
-  //
-  // TODO(tyoshino): This check should be performed also when request's
-  // origin is not same origin with the redirect destination's origin.
-  if (!(request_url.User().IsEmpty() && request_url.Pass().IsEmpty()))
-    return kRedirectContainsCredentials;
-
-  return kRedirectSuccess;
-}
-
-void CrossOriginAccessControl::RedirectErrorString(
-    StringBuilder& builder,
-    CrossOriginAccessControl::RedirectStatus status,
-    const KURL& request_url) {
-  switch (status) {
-    case kRedirectDisallowedScheme: {
-      builder.Append("Redirect location '");
-      builder.Append(request_url.GetString());
-      builder.Append("' has a disallowed scheme for cross-origin requests.");
-      return;
-    }
-    case kRedirectContainsCredentials: {
-      builder.Append("Redirect location '");
-      builder.Append(request_url.GetString());
-      builder.Append(
-          "' contains a username and password, which is disallowed for"
-          " cross-origin requests.");
-      return;
-    }
-    default:
-      NOTREACHED();
-  }
-}
-
-bool CrossOriginAccessControl::HandleRedirect(
-    RefPtr<SecurityOrigin> current_security_origin,
-    ResourceRequest& new_request,
-    const ResourceResponse& redirect_response,
-    WebURLRequest::FetchCredentialsMode credentials_mode,
-    ResourceLoaderOptions& options,
-    String& error_message) {
-  // http://www.w3.org/TR/cors/#redirect-steps terminology:
-  const KURL& last_url = redirect_response.Url();
-  const KURL& new_url = new_request.Url();
-
-  RefPtr<SecurityOrigin> new_security_origin = current_security_origin;
-
-  // TODO(tyoshino): This should be fixed to check not only the last one but
-  // all redirect responses.
-  if (!current_security_origin->CanRequest(last_url)) {
-    // Follow http://www.w3.org/TR/cors/#redirect-steps
-    CrossOriginAccessControl::RedirectStatus redirect_status =
-        CrossOriginAccessControl::CheckRedirectLocation(new_url);
-    if (redirect_status != kRedirectSuccess) {
-      StringBuilder builder;
-      builder.Append("Redirect from '");
-      builder.Append(last_url.GetString());
-      builder.Append("' has been blocked by CORS policy: ");
-      CrossOriginAccessControl::RedirectErrorString(builder, redirect_status,
-                                                    new_url);
-      error_message = builder.ToString();
-      return false;
-    }
-
-    // Step 5: perform resource sharing access check.
-    CrossOriginAccessControl::AccessStatus cors_status =
-        CrossOriginAccessControl::CheckAccess(
-            redirect_response, credentials_mode, current_security_origin.Get());
-    if (cors_status != kAccessAllowed) {
-      StringBuilder builder;
-      builder.Append("Redirect from '");
-      builder.Append(last_url.GetString());
-      builder.Append("' has been blocked by CORS policy: ");
-      CrossOriginAccessControl::AccessControlErrorString(
-          builder, cors_status, redirect_response,
-          current_security_origin.Get(), new_request.GetRequestContext());
-      error_message = builder.ToString();
-      return false;
-    }
-
-    RefPtr<SecurityOrigin> last_origin = SecurityOrigin::Create(last_url);
-    // Set request's origin to a globally unique identifier as specified in
-    // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch.
-    if (!last_origin->CanRequest(new_url)) {
-      options.security_origin = SecurityOrigin::CreateUnique();
-      new_security_origin = options.security_origin;
-    }
-  }
-
-  if (!current_security_origin->CanRequest(new_url)) {
-    new_request.ClearHTTPOrigin();
-    new_request.SetHTTPOrigin(new_security_origin.Get());
-
-    options.cors_flag = true;
-  }
-  return true;
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.h b/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.h
deleted file mode 100644
index 7238051..0000000
--- a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControl.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef CrossOriginAccessControl_h
-#define CrossOriginAccessControl_h
-
-#include "platform/PlatformExport.h"
-#include "platform/loader/fetch/ResourceLoaderOptions.h"
-#include "platform/loader/fetch/ResourceRequest.h"
-#include "platform/wtf/Allocator.h"
-#include "platform/wtf/Forward.h"
-#include "platform/wtf/HashSet.h"
-
-namespace blink {
-
-using HTTPHeaderSet = HashSet<String, CaseFoldingHash>;
-
-struct ResourceLoaderOptions;
-class ResourceRequest;
-class ResourceResponse;
-class SecurityOrigin;
-
-class PLATFORM_EXPORT CrossOriginAccessControl {
-  STATIC_ONLY(CrossOriginAccessControl);
-
- public:
-  // Enumerating the error conditions that the CORS
-  // access control check can report, including success.
-  //
-  // See |checkAccess()| and |accessControlErrorString()| which respectively
-  // produce and consume these error values, for precise meaning.
-  enum AccessStatus {
-    kAccessAllowed,
-    kInvalidResponse,
-    kAllowOriginMismatch,
-    kSubOriginMismatch,
-    kWildcardOriginNotAllowed,
-    kMissingAllowOriginHeader,
-    kMultipleAllowOriginValues,
-    kInvalidAllowOriginValue,
-    kDisallowCredentialsNotSetToTrue,
-  };
-
-  // Enumerating the error conditions that CORS preflight
-  // can report, including success.
-  //
-  // See |checkPreflight()| methods and |preflightErrorString()| which
-  // respectively produce and consume these error values, for precise meaning.
-  enum PreflightStatus {
-    kPreflightSuccess,
-    kPreflightInvalidStatus,
-    // "Access-Control-Allow-External:"
-    // ( https://wicg.github.io/cors-rfc1918/#headers ) specific error
-    // conditions:
-    kPreflightMissingAllowExternal,
-    kPreflightInvalidAllowExternal,
-  };
-
-  // Enumerating the error conditions that CORS redirect target URL
-  // checks can report, including success.
-  //
-  // See |checkRedirectLocation()| methods and |redirectErrorString()| which
-  // respectively produce and consume these error values, for precise meaning.
-  enum RedirectStatus {
-    kRedirectSuccess,
-    kRedirectDisallowedScheme,
-    kRedirectContainsCredentials,
-  };
-
-  // Perform a CORS access check on the response. Returns |kAccessAllowed| if
-  // access is allowed. Use |accessControlErrorString()| to construct a
-  // user-friendly error message for any of the other (error) conditions.
-  static AccessStatus CheckAccess(const ResourceResponse&,
-                                  WebURLRequest::FetchCredentialsMode,
-                                  const SecurityOrigin*);
-
-  // Perform the required CORS checks on the response to a preflight request.
-  // Returns |kPreflightSuccess| if preflight response was successful.
-  // Use |preflightErrorString()| to construct a user-friendly error message
-  // for any of the other (error) conditions.
-  static PreflightStatus CheckPreflight(const ResourceResponse&);
-
-  // Error checking for the currently experimental
-  // "Access-Control-Allow-External:" header. Shares error conditions with
-  // standard preflight checking.
-  static PreflightStatus CheckExternalPreflight(const ResourceResponse&);
-
-  // Given a redirected-to URL, check if the location is allowed
-  // according to CORS. That is:
-  // - the URL has a CORS supported scheme and
-  // - the URL does not contain the userinfo production.
-  //
-  // Returns |kRedirectSuccess| in all other cases. Use
-  // |redirectErrorString()| to construct a user-friendly error
-  // message for any of the error conditions.
-  static RedirectStatus CheckRedirectLocation(const KURL&);
-
-  static bool HandleRedirect(RefPtr<SecurityOrigin>,
-                             ResourceRequest&,
-                             const ResourceResponse&,
-                             WebURLRequest::FetchCredentialsMode,
-                             ResourceLoaderOptions&,
-                             String&);
-
-  // Stringify errors from CORS access checks, preflight or redirect checks.
-  static void AccessControlErrorString(StringBuilder&,
-                                       AccessStatus,
-                                       const ResourceResponse&,
-                                       const SecurityOrigin*,
-                                       WebURLRequest::RequestContext);
-  static void PreflightErrorString(StringBuilder&,
-                                   PreflightStatus,
-                                   const ResourceResponse&);
-  static void RedirectErrorString(StringBuilder&, RedirectStatus, const KURL&);
-
-  static bool IsOnAccessControlResponseHeaderWhitelist(const String&);
-
-  static ResourceRequest CreateAccessControlPreflightRequest(
-      const ResourceRequest&);
-
-  static void ParseAccessControlExposeHeadersAllowList(
-      const String& header_value,
-      HTTPHeaderSet&);
-
-  static void ExtractCorsExposedHeaderNamesList(const ResourceResponse&,
-                                                HTTPHeaderSet&);
-};
-
-}  // namespace blink
-
-#endif  // CrossOriginAccessControl_h
diff --git a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControlTest.cpp b/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControlTest.cpp
deleted file mode 100644
index 9bab708..0000000
--- a/third_party/WebKit/Source/platform/loader/fetch/CrossOriginAccessControlTest.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
-
-#include "platform/loader/fetch/ResourceRequest.h"
-#include "platform/weborigin/SecurityOrigin.h"
-#include "platform/wtf/RefPtr.h"
-#include "platform/wtf/text/WTFString.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-TEST(CreateAccessControlPreflightRequestTest, LexicographicalOrder) {
-  ResourceRequest request;
-  request.AddHTTPHeaderField("Orange", "Orange");
-  request.AddHTTPHeaderField("Apple", "Red");
-  request.AddHTTPHeaderField("Kiwifruit", "Green");
-  request.AddHTTPHeaderField("Content-Type", "application/octet-stream");
-  request.AddHTTPHeaderField("Strawberry", "Red");
-
-  ResourceRequest preflight =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(request);
-
-  EXPECT_EQ("apple,content-type,kiwifruit,orange,strawberry",
-            preflight.HttpHeaderField("Access-Control-Request-Headers"));
-}
-
-TEST(CreateAccessControlPreflightRequestTest, ExcludeSimpleHeaders) {
-  ResourceRequest request;
-  request.AddHTTPHeaderField("Accept", "everything");
-  request.AddHTTPHeaderField("Accept-Language", "everything");
-  request.AddHTTPHeaderField("Content-Language", "everything");
-  request.AddHTTPHeaderField("Save-Data", "on");
-
-  ResourceRequest preflight =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(request);
-
-  // Do not emit empty-valued headers; an empty list of non-"CORS safelisted"
-  // request headers should cause "Access-Control-Request-Headers:" to be
-  // left out in the preflight request.
-  EXPECT_EQ(g_null_atom,
-            preflight.HttpHeaderField("Access-Control-Request-Headers"));
-}
-
-TEST(CreateAccessControlPreflightRequestTest, ExcludeSimpleContentTypeHeader) {
-  ResourceRequest request;
-  request.AddHTTPHeaderField("Content-Type", "text/plain");
-
-  ResourceRequest preflight =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(request);
-
-  // Empty list also; see comment in test above.
-  EXPECT_EQ(g_null_atom,
-            preflight.HttpHeaderField("Access-Control-Request-Headers"));
-}
-
-TEST(CreateAccessControlPreflightRequestTest, IncludeNonSimpleHeader) {
-  ResourceRequest request;
-  request.AddHTTPHeaderField("X-Custom-Header", "foobar");
-
-  ResourceRequest preflight =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(request);
-
-  EXPECT_EQ("x-custom-header",
-            preflight.HttpHeaderField("Access-Control-Request-Headers"));
-}
-
-TEST(CreateAccessControlPreflightRequestTest,
-     IncludeNonSimpleContentTypeHeader) {
-  ResourceRequest request;
-  request.AddHTTPHeaderField("Content-Type", "application/octet-stream");
-
-  ResourceRequest preflight =
-      CrossOriginAccessControl::CreateAccessControlPreflightRequest(request);
-
-  EXPECT_EQ("content-type",
-            preflight.HttpHeaderField("Access-Control-Request-Headers"));
-}
-
-TEST(ParseAccessControlExposeHeadersAllowListTest, ValidInput) {
-  HTTPHeaderSet set;
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("valid",
-                                                                     set);
-  EXPECT_EQ(1U, set.size());
-  EXPECT_TRUE(set.Contains("valid"));
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("a,b",
-                                                                     set);
-  EXPECT_EQ(2U, set.size());
-  EXPECT_TRUE(set.Contains("a"));
-  EXPECT_TRUE(set.Contains("b"));
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-      "   a ,  b ", set);
-  EXPECT_EQ(2U, set.size());
-  EXPECT_TRUE(set.Contains("a"));
-  EXPECT_TRUE(set.Contains("b"));
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-      " \t   \t\t a", set);
-  EXPECT_EQ(1U, set.size());
-  EXPECT_TRUE(set.Contains("a"));
-}
-
-TEST(ParseAccessControlExposeHeadersAllowListTest, DuplicatedEntries) {
-  HTTPHeaderSet set;
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("a, a",
-                                                                     set);
-  EXPECT_EQ(1U, set.size());
-  EXPECT_TRUE(set.Contains("a"));
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("a, a, b",
-                                                                     set);
-  EXPECT_EQ(2U, set.size());
-  EXPECT_TRUE(set.Contains("a"));
-  EXPECT_TRUE(set.Contains("b"));
-}
-
-TEST(ParseAccessControlExposeHeadersAllowListTest, InvalidInput) {
-  HTTPHeaderSet set;
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-      "not valid", set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("///",
-                                                                     set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("/a/",
-                                                                     set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(",", set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(" , ",
-                                                                     set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(" , a",
-                                                                     set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("a , ",
-                                                                     set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList("", set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(" ", set);
-  EXPECT_TRUE(set.IsEmpty());
-
-  set.clear();
-  // U+0141 which is 'A' (0x41) + 0x100.
-  CrossOriginAccessControl::ParseAccessControlExposeHeadersAllowList(
-      String::FromUTF8("\xC5\x81"), set);
-  EXPECT_TRUE(set.IsEmpty());
-}
-
-}  // namespace
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp b/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
index 1221a67..36035f20b 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
@@ -25,7 +25,6 @@
 
 #include "platform/loader/fetch/FetchParameters.h"
 
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/ResourceFetcher.h"
 #include "platform/weborigin/KURL.h"
 #include "platform/weborigin/SecurityOrigin.h"
diff --git a/third_party/WebKit/Source/platform/loader/fetch/FetchUtils.h b/third_party/WebKit/Source/platform/loader/fetch/FetchUtils.h
index 789352a..16427cb7 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/FetchUtils.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/FetchUtils.h
@@ -39,7 +39,7 @@
   // Used by e.g. the CORS check algorithm to check if the FetchCredentialsMode
   // should be treated as equivalent to "include" in the Fetch spec.
   static bool ShouldTreatCredentialsModeAsInclude(
-      WebURLRequest::FetchCredentialsMode credentials_mode) {
+      const WebURLRequest::FetchCredentialsMode credentials_mode) {
     return credentials_mode == WebURLRequest::kFetchCredentialsModeInclude ||
            credentials_mode == WebURLRequest::kFetchCredentialsModePassword;
   }
diff --git a/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp b/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
index 155f2bb5e..2235fdd 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
@@ -38,7 +38,6 @@
 #include "platform/WebTaskRunner.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
 #include "platform/loader/fetch/CachedMetadata.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/FetchInitiatorTypeNames.h"
 #include "platform/loader/fetch/FetchParameters.h"
 #include "platform/loader/fetch/IntegrityMetadata.h"
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
index 259d153..440e1e2 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
@@ -32,7 +32,6 @@
 #include "platform/SharedBuffer.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/exported/WrappedResourceResponse.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/FetchContext.h"
 #include "platform/loader/fetch/Resource.h"
 #include "platform/loader/fetch/ResourceError.h"
@@ -44,6 +43,7 @@
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/text/StringBuilder.h"
 #include "public/platform/Platform.h"
+#include "public/platform/WebCORS.h"
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebData.h"
 #include "public/platform/WebURLError.h"
@@ -276,12 +276,14 @@
       RefPtr<SecurityOrigin> source_origin = options.security_origin;
       if (!source_origin.Get())
         source_origin = Context().GetSecurityOrigin();
-
-      String cors_error_msg;
-      if (!CrossOriginAccessControl::HandleRedirect(
-              source_origin, new_request, redirect_response,
-              fetch_credentials_mode, resource_->MutableOptions(),
-              cors_error_msg)) {
+      WebSecurityOrigin source_web_origin(source_origin.Get());
+      WrappedResourceRequest new_request_wrapper(new_request);
+      WebString cors_error_msg;
+      if (!WebCORS::HandleRedirect(source_web_origin, new_request_wrapper,
+                                   WrappedResourceResponse(redirect_response),
+                                   fetch_credentials_mode,
+                                   resource_->MutableOptions(),
+                                   cors_error_msg)) {
         resource_->SetCORSStatus(CORSStatus::kFailed);
 
         if (!unused_preload) {
@@ -293,6 +295,8 @@
                                           ResourceRequestBlockedReason::kOther);
         return false;
       }
+
+      source_origin = source_web_origin;
     }
     if (resource_type == Resource::kImage &&
         fetcher_->ShouldDeferImageLoad(new_url)) {
@@ -427,12 +431,12 @@
           ? resource_->GetResponse()
           : response;
 
-  CrossOriginAccessControl::AccessStatus cors_status =
-      CrossOriginAccessControl::CheckAccess(
-          response_for_access_control,
-          initial_request.GetFetchCredentialsMode(), source_origin);
+  WebCORS::AccessStatus cors_status =
+      WebCORS::CheckAccess(WrappedResourceResponse(response_for_access_control),
+                           initial_request.GetFetchCredentialsMode(),
+                           WebSecurityOrigin(source_origin));
 
-  if (cors_status == CrossOriginAccessControl::AccessStatus::kAccessAllowed)
+  if (cors_status == WebCORS::AccessStatus::kAccessAllowed)
     return CORSStatus::kSuccessful;
 
   String resource_type = Resource::ResourceTypeToString(
@@ -444,10 +448,9 @@
   error_msg.Append("' from origin '");
   error_msg.Append(source_origin->ToString());
   error_msg.Append("' has been blocked by CORS policy: ");
-
-  CrossOriginAccessControl::AccessControlErrorString(
-      error_msg, cors_status, response_for_access_control, source_origin,
-      initial_request.GetRequestContext());
+  error_msg.Append(WebCORS::AccessControlErrorString(
+      cors_status, WrappedResourceResponse(response_for_access_control),
+      WebSecurityOrigin(source_origin), initial_request.GetRequestContext()));
 
   return CORSStatus::kFailed;
 }
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.h b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.h
index 1488621..275d101c3 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.h
@@ -34,7 +34,6 @@
 #include "platform/PlatformExport.h"
 #include "platform/heap/Handle.h"
 #include "platform/heap/SelfKeepAlive.h"
-#include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/loader/fetch/Resource.h"
 #include "platform/loader/fetch/ResourceLoadScheduler.h"
 #include "platform/loader/fetch/ResourceLoaderOptions.h"
diff --git a/third_party/WebKit/Source/platform/network/HTTPNames.json5 b/third_party/WebKit/Source/platform/network/HTTPNames.json5
index 2d504aa..4fb891c8 100644
--- a/third_party/WebKit/Source/platform/network/HTTPNames.json5
+++ b/third_party/WebKit/Source/platform/network/HTTPNames.json5
@@ -16,6 +16,8 @@
     "Access-Control-Allow-Headers",
     "Access-Control-Allow-Methods",
     "Access-Control-Allow-Origin",
+    "Access-Control-Allow-Suborigin",
+    "Access-Control-Allow-Credentials",
     "Access-Control-Expose-Headers",
     "Access-Control-Max-Age",
     "Access-Control-Request-External",
diff --git a/third_party/WebKit/public/platform/WebCORS.h b/third_party/WebKit/public/platform/WebCORS.h
new file mode 100644
index 0000000..5c528329
--- /dev/null
+++ b/third_party/WebKit/public/platform/WebCORS.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WebCORS_h
+#define WebCORS_h
+
+#include "platform/loader/fetch/ResourceLoaderOptions.h"
+#include "platform/wtf/HashSet.h"
+#include "platform/wtf/text/StringHash.h"
+#include "public/platform/WebString.h"
+#include "public/platform/WebURL.h"
+#include "public/platform/WebURLRequest.h"
+
+namespace blink {
+
+class WebURLResponse;
+class WebSecurityOrigin;
+
+namespace WebCORS {
+
+typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet;
+
+// Enumerating the error conditions that the CORS
+// access control check can report, including success.
+//
+// See |CheckAccess()| and |AccessControlErrorString()| which respectively
+// produce and consume these error values, for precise meaning.
+enum AccessStatus {
+  kAccessAllowed,
+  kInvalidResponse,
+  kAllowOriginMismatch,
+  kSubOriginMismatch,
+  kWildcardOriginNotAllowed,
+  kMissingAllowOriginHeader,
+  kMultipleAllowOriginValues,
+  kInvalidAllowOriginValue,
+  kDisallowCredentialsNotSetToTrue,
+};
+
+// Enumerating the error conditions that CORS preflight
+// can report, including success.
+//
+// See |CheckPreflight()| methods and |PreflightErrorString()| which
+// respectively produce and consume these error values, for precise meaning.
+enum PreflightStatus {
+  kPreflightSuccess,
+  kPreflightInvalidStatus,
+  // "Access-Control-Allow-External:"
+  // ( https://wicg.github.io/cors-rfc1918/#headers ) specific error
+  // conditions:
+  kPreflightMissingAllowExternal,
+  kPreflightInvalidAllowExternal,
+};
+
+// Enumerating the error conditions that CORS redirect target URL
+// checks can report, including success.
+//
+// See |CheckRedirectLocation()| methods and |RedirectErrorString()| which
+// respectively produce and consume these error values, for precise meaning.
+enum RedirectStatus {
+  kRedirectSuccess,
+  kRedirectDisallowedScheme,
+  kRedirectContainsCredentials,
+};
+
+// Perform a CORS access check on the response. Returns |kAccessAllowed| if
+// access is allowed. Use |AccessControlErrorString()| to construct a
+// user-friendly error message for any of the other (error) conditions.
+BLINK_PLATFORM_EXPORT AccessStatus
+CheckAccess(const WebURLResponse&,
+            WebURLRequest::FetchCredentialsMode,
+            const WebSecurityOrigin&);
+
+// Given a redirected-to URL, check if the location is allowed
+// according to CORS. That is:
+// - the URL has a CORS supported scheme and
+// - the URL does not contain the userinfo production.
+//
+// Returns |kRedirectSuccess| in all other cases. Use
+// |RedirectErrorString()| to construct a user-friendly error
+// message for any of the error conditions.
+BLINK_PLATFORM_EXPORT RedirectStatus CheckRedirectLocation(const WebURL&);
+
+// Perform the required CORS checks on the response to a preflight request.
+// Returns |kPreflightSuccess| if preflight response was successful.
+// Use |PreflightErrorString()| to construct a user-friendly error message
+// for any of the other (error) conditions.
+BLINK_PLATFORM_EXPORT PreflightStatus CheckPreflight(const WebURLResponse&);
+
+// Error checking for the currently experimental
+// "Access-Control-Allow-External:" header. Shares error conditions with
+// standard preflight checking.
+BLINK_PLATFORM_EXPORT PreflightStatus
+CheckExternalPreflight(const WebURLResponse&);
+
+BLINK_PLATFORM_EXPORT WebURLRequest
+CreateAccessControlPreflightRequest(const WebURLRequest&);
+
+// TODO(tyoshino): Using platform/loader/fetch/ResourceLoaderOptions violates
+// the DEPS rule. This will be fixed soon by making HandleRedirect() not
+// depending on ResourceLoaderOptions.
+BLINK_PLATFORM_EXPORT bool HandleRedirect(WebSecurityOrigin&,
+                                          WebURLRequest&,
+                                          const WebURLResponse&,
+                                          WebURLRequest::FetchCredentialsMode,
+                                          ResourceLoaderOptions&,
+                                          WebString&);
+
+// Stringify errors from CORS access checks, preflight or redirect checks.
+BLINK_PLATFORM_EXPORT WebString
+AccessControlErrorString(const AccessStatus,
+                         const WebURLResponse&,
+                         const WebSecurityOrigin&,
+                         const WebURLRequest::RequestContext);
+
+BLINK_PLATFORM_EXPORT WebString PreflightErrorString(const PreflightStatus,
+                                                     const WebURLResponse&);
+
+BLINK_PLATFORM_EXPORT WebString RedirectErrorString(const RedirectStatus,
+                                                    const WebURL&);
+
+BLINK_PLATFORM_EXPORT void ParseAccessControlExposeHeadersAllowList(
+    const WebString&,
+    HTTPHeaderSet&);
+
+BLINK_PLATFORM_EXPORT void ExtractCorsExposedHeaderNamesList(
+    const WebURLResponse&,
+    HTTPHeaderSet&);
+
+BLINK_PLATFORM_EXPORT bool IsOnAccessControlResponseHeaderWhitelist(
+    const WebString&);
+
+}  // namespace WebCORS
+
+}  // namespace blink
+
+#endif  // WebCORS_h
diff --git a/third_party/WebKit/public/platform/WebURLResponse.h b/third_party/WebKit/public/platform/WebURLResponse.h
index 1473594b..23efd60 100644
--- a/third_party/WebKit/public/platform/WebURLResponse.h
+++ b/third_party/WebKit/public/platform/WebURLResponse.h
@@ -249,6 +249,7 @@
 
   // The headers that should be exposed according to CORS. Only guaranteed
   // to be set if the response was served by a ServiceWorker.
+  BLINK_PLATFORM_EXPORT WebVector<WebString> CorsExposedHeaderNames() const;
   BLINK_PLATFORM_EXPORT void SetCorsExposedHeaderNames(
       const WebVector<WebString>&);
 
diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py
index ff2ae57..55500ad 100755
--- a/tools/gn/bootstrap/bootstrap.py
+++ b/tools/gn/bootstrap/bootstrap.py
@@ -550,8 +550,6 @@
       'base/trace_event/memory_peak_detector.cc',
       'base/trace_event/memory_usage_estimator.cc',
       'base/trace_event/process_memory_dump.cc',
-      'base/trace_event/process_memory_maps.cc',
-      'base/trace_event/process_memory_totals.cc',
       'base/trace_event/sharded_allocation_register.cc',
       'base/trace_event/trace_buffer.cc',
       'base/trace_event/trace_config.cc',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index bf3ca92..4f8db58 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -54361,11 +54361,17 @@
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.Completed" enum="BooleanHit">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>When the merchant has processed the user's Payment Request.</summary>
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.Initiated" enum="BooleanHit">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>When a Payment Request gets initiated by the user.</summary>
 </histogram>
@@ -54379,6 +54385,9 @@
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.PayClicked" enum="BooleanHit">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>
     When the user clicks the &quot;pay&quot; button in the Payment Request UI.
@@ -54387,6 +54396,9 @@
 
 <histogram name="PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails"
     enum="BooleanHit">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>
     When the browser retrieves the instrument details from the payment app to
@@ -54395,6 +54407,9 @@
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.Shown" enum="BooleanHit">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>
     When the Payment Request UI gets shown after initialization.
@@ -54403,6 +54418,9 @@
 
 <histogram name="PaymentRequest.CheckoutFunnel.SkippedShow"
     enum="BooleanSkipped">
+  <obsolete>
+    M62+ Part of PaymentRequest.Events
+  </obsolete>
   <owner>sebsg@chromium.org</owner>
   <summary>
     When the Payment Request UI gets skipped to go directly to the payment app.
diff --git a/tools/perf/benchmarks/loading.py b/tools/perf/benchmarks/loading.py
index 93b69b9..cffbf0e 100644
--- a/tools/perf/benchmarks/loading.py
+++ b/tools/perf/benchmarks/loading.py
@@ -42,7 +42,9 @@
     class StoryExpectations(story.expectations.StoryExpectations):
       def SetExpectations(self):
         self.DisableStory(
-            'uol.com.br', [story.expectations.ALL_LINUX], 'crbug.com/723783')
+            'uol.com.br', [story.expectations.ALL_LINUX], 'crbug.com/752611')
+        self.DisableStory(
+            'Orange', [story.expectations.ALL_WIN], 'crbug.com/723783')
     return StoryExpectations()
 
   @classmethod
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index 9ef93ce4..f664fd27 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -588,7 +588,6 @@
     return StoryExpectations()
 
 
-@benchmark.Disabled('android')  # http://crbug.com/513699
 @benchmark.Owner(emails=['cblume@chromium.org'])
 class SmoothnessGpuImageDecodingCases(_Smoothness):
   """Measures decoding statistics for jpeg images with GPU rasterization.
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index 8d4bf832..4294c4f 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -1700,8 +1700,8 @@
   EXPECT_TRUE(delegate->tap_cancel());
   EXPECT_TRUE(delegate->scroll_begin());
   EXPECT_TRUE(delegate->scroll_update());
-  EXPECT_EQ(15, delegate->scroll_x_hint());
-  EXPECT_EQ(15, delegate->scroll_y_hint());
+  EXPECT_EQ(10, delegate->scroll_x_hint());
+  EXPECT_EQ(10, delegate->scroll_y_hint());
 
   delegate->Reset();
 
@@ -3709,7 +3709,7 @@
   EXPECT_TRUE(delegate->scroll_update());
   // 3 px consumed by touch slop region.
   EXPECT_EQ(-1, delegate->scroll_y());
-  EXPECT_EQ(-4, delegate->scroll_y_hint());
+  EXPECT_EQ(-1, delegate->scroll_y_hint());
 
   delegate->Reset();
 
@@ -4096,7 +4096,7 @@
   EXPECT_TRUE(delegate->scroll_begin());
   EXPECT_TRUE(delegate->scroll_update());
   EXPECT_NEAR(0.1, delegate->scroll_x(), 0.0001);
-  EXPECT_FLOAT_EQ(3.1f, delegate->scroll_x_hint());
+  EXPECT_NEAR(0.1, delegate->scroll_x_hint(), 0.0001);
   delegate->Reset();
 
   ui::TouchEvent move4(
diff --git a/ui/aura/mus/mus_context_factory.cc b/ui/aura/mus/mus_context_factory.cc
index 1da45b6..6cab93e48 100644
--- a/ui/aura/mus/mus_context_factory.cc
+++ b/ui/aura/mus/mus_context_factory.cc
@@ -8,11 +8,11 @@
 #include "base/memory/ptr_util.h"
 #include "cc/base/switches.h"
 #include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/host/renderer_settings_creation.h"
 #include "services/ui/public/cpp/gpu/gpu.h"
 #include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/compositor/compositor_switches.h"
-#include "ui/compositor/compositor_util.h"
 #include "ui/display/display_switches.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_bindings.h"
@@ -40,7 +40,7 @@
 MusContextFactory::MusContextFactory(ui::Gpu* gpu)
     : gpu_(gpu),
       resource_settings_(
-          ui::CreateResourceSettings(CreateBufferToTextureTargetMap())),
+          viz::CreateResourceSettings(CreateBufferToTextureTargetMap())),
       weak_ptr_factory_(this) {}
 
 MusContextFactory::~MusContextFactory() {}
diff --git a/ui/base/ui_base_switches.cc b/ui/base/ui_base_switches.cc
index ed2ef37..4267481 100644
--- a/ui/base/ui_base_switches.cc
+++ b/ui/base/ui_base_switches.cc
@@ -31,6 +31,9 @@
 const char kEnableMergeKeyCharEvents[]     = "enable-merge-key-char-events";
 #endif
 
+// Disables layer-edge anti-aliasing in the compositor.
+const char kDisableCompositedAntialiasing[] = "disable-composited-antialiasing";
+
 // Disables use of DWM composition for top level windows.
 const char kDisableDwmComposition[] = "disable-dwm-composition";
 
@@ -43,6 +46,13 @@
 // Enables touch event based drag and drop.
 const char kEnableTouchDragDrop[] = "enable-touch-drag-drop";
 
+// TODO(dcastagna): Draw debug quad borders only when it is actually
+// an overlay candidate.
+// Renders a border around GL composited overlay candidate quads to
+// help debug and study overlay support.
+const char kGlCompositedOverlayCandidateQuadBorder[] =
+    "gl-composited-overlay-candidate-quad-border";
+
 // The language file that we want to try to open. Of the form
 // language[-country] where language is the 2 letter code from ISO-639.
 const char kLang[] = "lang";
@@ -79,4 +89,24 @@
 // throughout Chrome (not just top Chrome).
 const char kExtendMdToSecondaryUi[] = "secondary-ui-md";
 
+// Disable partial swap which is needed for some OpenGL drivers / emulators.
+const char kUIDisablePartialSwap[] = "ui-disable-partial-swap";
+
+// Visualize overdraw by color-coding elements based on if they have other
+// elements drawn underneath. This is good for showing where the UI might be
+// doing more rendering work than necessary. The colors are hinting at the
+// amount of overdraw on your screen for each pixel, as follows:
+//
+// True color: No overdraw.
+// Blue: Overdrawn once.
+// Green: Overdrawn twice.
+// Pink: Overdrawn three times.
+// Red: Overdrawn four or more times.
+const char kShowOverdrawFeedback[] = "show-overdraw-feedback";
+
+// Disable re-use of non-exact resources to fulfill ResourcePool requests.
+// Intended only for use in layout or pixel tests to reduce noise.
+const char kDisallowNonExactResourceReuse[] =
+    "disallow-non-exact-resource-reuse";
+
 }  // namespace switches
diff --git a/ui/base/ui_base_switches.h b/ui/base/ui_base_switches.h
index 69e8cc1..b3a4634 100644
--- a/ui/base/ui_base_switches.h
+++ b/ui/base/ui_base_switches.h
@@ -8,6 +8,7 @@
 #define UI_BASE_UI_BASE_SWITCHES_H_
 
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "ui/base/ui_base_export.h"
 
@@ -25,11 +26,13 @@
 UI_BASE_EXPORT extern const char kEnableMergeKeyCharEvents[];
 #endif
 
+UI_BASE_EXPORT extern const char kDisableCompositedAntialiasing[];
 UI_BASE_EXPORT extern const char kDisableDwmComposition[];
 UI_BASE_EXPORT extern const char kDisableNewVirtualKeyboardBehavior[];
 UI_BASE_EXPORT extern const char kDisableTouchAdjustment[];
 UI_BASE_EXPORT extern const char kDisableTouchDragDrop[];
 UI_BASE_EXPORT extern const char kEnableTouchDragDrop[];
+UI_BASE_EXPORT extern const char kGlCompositedOverlayCandidateQuadBorder[];
 UI_BASE_EXPORT extern const char kLang[];
 UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeed[];
 UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeedFast[];
@@ -40,6 +43,11 @@
 UI_BASE_EXPORT extern const char kTopChromeMDMaterialHybrid[];
 UI_BASE_EXPORT extern const char kTopChromeMDNonMaterial[];
 UI_BASE_EXPORT extern const char kExtendMdToSecondaryUi[];
+UI_BASE_EXPORT extern const char kUIDisablePartialSwap[];
+UI_BASE_EXPORT extern const char kShowOverdrawFeedback[];
+
+// Test related.
+UI_BASE_EXPORT extern const char kDisallowNonExactResourceReuse[];
 
 }  // namespace switches
 
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn
index 27ea8d3..114ac26 100644
--- a/ui/compositor/BUILD.gn
+++ b/ui/compositor/BUILD.gn
@@ -27,8 +27,6 @@
     "compositor_observer.h",
     "compositor_switches.cc",
     "compositor_switches.h",
-    "compositor_util.cc",
-    "compositor_util.h",
     "compositor_vsync_manager.cc",
     "compositor_vsync_manager.h",
     "debug_utils.cc",
@@ -92,6 +90,7 @@
     "//components/viz/service",
     "//gpu/command_buffer/common",
     "//skia",
+    "//ui/base",
     "//ui/display",
     "//ui/gfx",
     "//ui/gfx/animation",
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 262ac1f..fe79595 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -39,6 +39,7 @@
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/compositor/compositor_observer.h"
 #include "ui/compositor/compositor_switches.h"
 #include "ui/compositor/compositor_vsync_manager.h"
@@ -170,7 +171,7 @@
       gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
 
   settings.disallow_non_exact_resource_reuse =
-      command_line->HasSwitch(cc::switches::kDisallowNonExactResourceReuse);
+      command_line->HasSwitch(switches::kDisallowNonExactResourceReuse);
 
   settings.wait_for_all_pipeline_stages_before_draw =
       command_line->HasSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw);
diff --git a/ui/compositor/compositor_switches.cc b/ui/compositor/compositor_switches.cc
index 4833b4f..9d6c470c 100644
--- a/ui/compositor/compositor_switches.cc
+++ b/ui/compositor/compositor_switches.cc
@@ -21,9 +21,6 @@
 // maximum.
 const char kLimitFps[] = "limit-fps";
 
-// Disable partial swap which is needed for some OpenGL drivers / emulators.
-const char kUIDisablePartialSwap[] = "ui-disable-partial-swap";
-
 const char kUIEnableRGBA4444Textures[] = "ui-enable-rgba-4444-textures";
 
 const char kUIEnableZeroCopy[] = "ui-enable-zero-copy";
diff --git a/ui/compositor/compositor_switches.h b/ui/compositor/compositor_switches.h
index 9f669a4..5c03be48 100644
--- a/ui/compositor/compositor_switches.h
+++ b/ui/compositor/compositor_switches.h
@@ -12,7 +12,6 @@
 COMPOSITOR_EXPORT extern const char kEnableHardwareOverlays[];
 COMPOSITOR_EXPORT extern const char kEnablePixelOutputInTests[];
 COMPOSITOR_EXPORT extern const char kLimitFps[];
-COMPOSITOR_EXPORT extern const char kUIDisablePartialSwap[];
 COMPOSITOR_EXPORT extern const char kUIEnableRGBA4444Textures[];
 COMPOSITOR_EXPORT extern const char kUIEnableZeroCopy[];
 COMPOSITOR_EXPORT extern const char kUIShowPaintRects[];
diff --git a/ui/compositor/compositor_util.h b/ui/compositor/compositor_util.h
deleted file mode 100644
index 40e31b6..0000000
--- a/ui/compositor/compositor_util.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_COMPOSITOR_COMPOSITOR_UTIL_H_
-#define UI_COMPOSITOR_COMPOSITOR_UTIL_H_
-
-#include <stdint.h>
-
-#include "components/viz/common/resources/buffer_to_texture_target_map.h"
-#include "ui/compositor/compositor_export.h"
-
-namespace viz {
-class RendererSettings;
-class ResourceSettings;
-}
-
-namespace ui {
-
-// |image_targets| is a map from every supported pair of GPU memory buffer
-// usage/format to its GL texture target.
-COMPOSITOR_EXPORT viz::ResourceSettings CreateResourceSettings(
-    const viz::BufferToTextureTargetMap& image_targets);
-
-COMPOSITOR_EXPORT viz::RendererSettings CreateRendererSettings(
-    const viz::BufferToTextureTargetMap& image_targets);
-
-}  // namespace ui
-
-#endif  // UI_COMPOSITOR_COMPOSITOR_UTIL_H_
diff --git a/ui/events/gesture_detection/gesture_provider.cc b/ui/events/gesture_detection/gesture_provider.cc
index 2c0c9bf..39b8a2d 100644
--- a/ui/events/gesture_detection/gesture_provider.cc
+++ b/ui/events/gesture_detection/gesture_provider.cc
@@ -323,8 +323,8 @@
     if (!scroll_event_sent_) {
       // Note that scroll start hints are in distance traveled, where
       // scroll deltas are in the opposite direction.
-      GestureEventDetails scroll_details(
-          ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
+      GestureEventDetails scroll_details(ET_GESTURE_SCROLL_BEGIN, -distance_x,
+                                         -distance_y);
       scroll_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
 
       // Scroll focus point always starts with the first touch down point.
diff --git a/ui/events/gesture_detection/gesture_provider_unittest.cc b/ui/events/gesture_detection/gesture_provider_unittest.cc
index ad6d1c98..50390db 100644
--- a/ui/events/gesture_detection/gesture_provider_unittest.cc
+++ b/ui/events/gesture_detection/gesture_provider_unittest.cc
@@ -917,8 +917,11 @@
 // Generate a scroll gesture and verify that the resulting scroll begin event
 // has the expected hint values.
 TEST_F(GestureProviderTest, ScrollBeginValues) {
-  const float delta_x = 13;
-  const float delta_y = 89;
+  const float delta_x = 14;
+  const float delta_y = 48;
+  // These are the deltas after subtracting slop region and railing.
+  const float delta_x_hint = 0;
+  const float delta_y_hint = 40.32f;
 
   const base::TimeTicks event_time = TimeTicks::Now();
 
@@ -944,8 +947,8 @@
 
   const GestureEventData* scroll_begin_gesture = GetActiveScrollBeginEvent();
   ASSERT_TRUE(scroll_begin_gesture);
-  EXPECT_EQ(delta_x, scroll_begin_gesture->details.scroll_x_hint());
-  EXPECT_EQ(delta_y, scroll_begin_gesture->details.scroll_y_hint());
+  EXPECT_EQ(delta_x_hint, scroll_begin_gesture->details.scroll_x_hint());
+  EXPECT_EQ(delta_y_hint, scroll_begin_gesture->details.scroll_y_hint());
 }
 
 // The following three tests verify that slop regions are checked for
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
index fbe0fef..517bd40 100644
--- a/ui/gfx/native_widget_types.h
+++ b/ui/gfx/native_widget_types.h
@@ -133,12 +133,6 @@
 typedef ui::ViewAndroid* NativeView;
 typedef ui::WindowAndroid* NativeWindow;
 typedef jobject NativeEvent;
-#elif defined(OS_FUCHSIA)
-// TODO(fuchsia): Update when we have a plan for UI on Fuchsia.
-typedef void* NativeCursor;
-typedef void* NativeView;
-typedef void* NativeWindow;
-typedef void* NativeEvent;
 #else
 #error Unknown build environment.
 #endif
@@ -203,10 +197,6 @@
 #elif defined(USE_OZONE)
 typedef int32_t AcceleratedWidget;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(OS_FUCHSIA)
-// TODO(fuchsia): Update when we have a plan for UI on Fuchsia.
-typedef void* AcceleratedWidget;
-constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
 #else
 #error unknown platform
 #endif
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index 1856e89..3124f826 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -118,7 +118,8 @@
 InkDropHostView::InkDropHostView()
     : ink_drop_mode_(InkDropMode::OFF),
       ink_drop_(nullptr),
-      ink_drop_visible_opacity_(kInkDropVisibleOpacity),
+      ink_drop_visible_opacity_(
+          PlatformStyle::kUseRipples ? kInkDropVisibleOpacity : 0),
       old_paint_to_layer_(false),
       destroying_(false) {}
 
@@ -277,7 +278,7 @@
 
 InkDrop* InkDropHostView::GetInkDrop() {
   if (!ink_drop_) {
-    if (ink_drop_mode_ == InkDropMode::OFF || !PlatformStyle::kUseRipples)
+    if (ink_drop_mode_ == InkDropMode::OFF)
       ink_drop_ = base::MakeUnique<InkDropStub>();
     else
       ink_drop_ = CreateInkDrop();
diff --git a/ui/views/controls/button/toggle_button_unittest.cc b/ui/views/controls/button/toggle_button_unittest.cc
index e0a6e2e8..ac61914 100644
--- a/ui/views/controls/button/toggle_button_unittest.cc
+++ b/ui/views/controls/button/toggle_button_unittest.cc
@@ -10,7 +10,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
-#include "ui/views/style/platform_style.h"
 #include "ui/views/test/views_test_base.h"
 
 namespace views {
@@ -95,11 +94,7 @@
   button()->OnMousePressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  // On platforms with no ripples, there should never be an ink drop layer.
-  if (PlatformStyle::kUseRipples)
-    EXPECT_EQ(1, counter());
-  else
-    EXPECT_EQ(0, counter());
+  EXPECT_EQ(1, counter());
   delete button();
   EXPECT_EQ(0, counter());
 }
@@ -108,10 +103,7 @@
 // ToggleButton has focus (and is showing a ripple).
 TEST_F(ToggleButtonTest, ShutdownWithFocus) {
   button()->RequestFocus();
-  if (PlatformStyle::kUseRipples)
-    EXPECT_EQ(1, counter());
-  else
-    EXPECT_EQ(0, counter());
+  EXPECT_EQ(1, counter());
 }
 
 // Verify that ToggleButton::accepts_events_ works as expected.