diff --git a/DEPS b/DEPS
index 296d6a7..b96737a 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'aeef5610d7a3fe4a53fc27b20486da79bf7a949a',
+  'skia_revision': '7adde145d3913cfd67b90bf83a9ea54386a285a7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'ad889a602199a98d5f2605c6cb346071199df55f',
+  'v8_revision': '5d22cf824391a2049c91e15bad9488a4ce57321a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'b4533de1fdf7f5ac2fb4824582e13cb55ca94dd8',
+  'angle_revision': '6a6b09c9b62b003ca96b036d0249db5ce99cc269',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -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': '4bd198f6a85c700acbbd23c0b666c846636526d2',
+  'catapult_revision': 'e6b04fef1997055d90390256fe96282bbbd98a51',
   # 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/metrics/histogram_macros.h b/base/metrics/histogram_macros.h
index 9e3caec..7847376 100644
--- a/base/metrics/histogram_macros.h
+++ b/base/metrics/histogram_macros.h
@@ -256,6 +256,39 @@
     INTERNAL_HISTOGRAM_SPARSE_SLOWLY(name, sample)
 
 //------------------------------------------------------------------------------
+// Histogram instantiation helpers.
+
+// Support a collection of histograms, perhaps one for each entry in an
+// enumeration. This macro manages a block of pointers, adding to a specific
+// one by its index.
+//
+// A typical instantiation looks something like this:
+//  STATIC_HISTOGRAM_POINTER_GROUP(
+//      GetHistogramNameForIndex(histogram_index),
+//      histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta),
+//      base::Histogram::FactoryGet(
+//          GetHistogramNameForIndex(histogram_index),
+//          MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
+//          base::HistogramBase::kUmaTargetedHistogramFlag));
+//
+// Though it seems inefficient to generate the name twice, the first
+// instance will be used only for DCHECK builds and the second will
+// execute only during the first access to the given index, after which
+// the pointer is cached and the name never needed again.
+#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index,        \
+                                       constant_maximum,                      \
+                                       histogram_add_method_invocation,       \
+                                       histogram_factory_get_invocation)      \
+  do {                                                                        \
+    static base::subtle::AtomicWord atomic_histograms[constant_maximum];      \
+    DCHECK_LE(0, index);                                                      \
+    DCHECK_LT(index, constant_maximum);                                       \
+    HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \
+                          histogram_add_method_invocation,                    \
+                          histogram_factory_get_invocation);                  \
+  } while (0)
+
+//------------------------------------------------------------------------------
 // Deprecated histogram macros. Not recommended for current use.
 
 // Legacy name for UMA_HISTOGRAM_COUNTS_1M. Suggest using explicit naming
diff --git a/base/task_scheduler/task_traits.h b/base/task_scheduler/task_traits.h
index 6f6ab09..8354650 100644
--- a/base/task_scheduler/task_traits.h
+++ b/base/task_scheduler/task_traits.h
@@ -141,10 +141,6 @@
   // Returns true if tasks with these traits may use base/ sync primitives.
   bool with_base_sync_primitives() const { return with_base_sync_primitives_; }
 
-  // DEPRECATED
-  // TODO(fdoray): Remove this as part of crbug.com/675660
-  bool with_file_io() const { return may_block(); }
-
   // Returns the priority of tasks with these traits.
   TaskPriority priority() const { return priority_; }
 
diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc
index d767895..0932e64a 100644
--- a/base/third_party/symbolize/symbolize.cc
+++ b/base/third_party/symbolize/symbolize.cc
@@ -56,6 +56,7 @@
 
 #if defined(HAVE_SYMBOLIZE)
 
+#include <algorithm>
 #include <limits>
 
 #include "symbolize.h"
@@ -296,10 +297,12 @@
 
     // Read at most NUM_SYMBOLS symbols at once to save read() calls.
     ElfW(Sym) buf[NUM_SYMBOLS];
-    const ssize_t len = ReadFromOffset(fd, &buf, sizeof(buf), offset);
+    int num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i);
+    const ssize_t len =
+        ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset);
     SAFE_ASSERT(len % sizeof(buf[0]) == 0);
     const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
-    SAFE_ASSERT(num_symbols_in_buf <= sizeof(buf)/sizeof(buf[0]));
+    SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read);
     for (int j = 0; j < num_symbols_in_buf; ++j) {
       const ElfW(Sym)& symbol = buf[j];
       uint64_t start_address = symbol.st_value;
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index be917fd..8f3beb1 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -52,6 +52,9 @@
 }
 namespace mojo {
 class SyncCallRestrictions;
+namespace edk {
+class ScopedIPCSupport;
+}
 }
 namespace ui {
 class CommandBufferClientImpl;
@@ -193,6 +196,7 @@
   friend class PlatformThread;
   friend class android::JavaHandlerThread;
   friend class mojo::SyncCallRestrictions;
+  friend class mojo::edk::ScopedIPCSupport;
   friend class ui::CommandBufferClientImpl;
   friend class ui::CommandBufferLocal;
   friend class ui::GpuState;
diff --git a/blimp/engine/app/blimp_engine_crash_keys.cc b/blimp/engine/app/blimp_engine_crash_keys.cc
index 848521c3..dd2a258 100644
--- a/blimp/engine/app/blimp_engine_crash_keys.cc
+++ b/blimp/engine/app/blimp_engine_crash_keys.cc
@@ -103,6 +103,8 @@
       // Temporary for https://crbug.com/668633.
       { "swdh_set_hosted_version_worker_pid", crash_keys::kSmallSize },
       { "swdh_set_hosted_version_host_pid", crash_keys::kSmallSize },
+      { "swdh_set_hosted_version_is_new_process", crash_keys::kSmallSize },
+      { "swdh_set_hosted_version_restart_count", crash_keys::kSmallSize },
     };
 
   return base::debug::InitCrashKeys(engine_keys, arraysize(engine_keys),
diff --git a/build/config/ios/BUILD.gn b/build/config/ios/BUILD.gn
index 0dda3e5..ec6b9e6 100644
--- a/build/config/ios/BUILD.gn
+++ b/build/config/ios/BUILD.gn
@@ -6,6 +6,23 @@
 import("//build/config/sysroot.gni")
 import("//build/toolchain/toolchain.gni")
 
+declare_args() {
+  # Enabling this option makes clang compile to an intermediate
+  # representation ("bitcode"), and not to native code. This is preferred
+  # when including WebRTC in the apps that will be sent to Apple's App Store
+  # and mandatory for the apps that run on watchOS or tvOS.
+  # The option only works when building with Xcode (use_xcode_clang = true).
+  # Mimicking how Xcode handles it, the production builds (is_debug = false)
+  # get real bitcode sections added, while the debug builds (is_debug = true)
+  # only get bitcode-section "markers" added in them.
+  # NOTE: This option is ignored when building versions for the iOS simulator,
+  # where a part of libvpx is compiled from the assembly code written using
+  # Intel assembly syntax; Yasm / Nasm do not support emitting bitcode parts.
+  # That is not a limitation for now as Xcode mandates the presence of bitcode
+  # only when building bitcode-enabled projects for real devices (ARM CPUs).
+  enable_ios_bitcode = false
+}
+
 # This is included by reference in the //build/config/compiler:runtime_library
 # config that is applied to all targets. It is here to separate out the logic
 # that is iOS-only. Please see that target for advice on what should go in
@@ -24,6 +41,14 @@
     common_flags += [ "-miphoneos-version-min=$ios_deployment_target" ]
   }
 
+  if (use_xcode_clang && enable_ios_bitcode && !use_ios_simulator) {
+    if (is_debug) {
+      common_flags += [ "-fembed-bitcode-marker" ]
+    } else {
+      common_flags += [ "-fembed-bitcode" ]
+    }
+  }
+
   asmflags = common_flags
   cflags = common_flags
   ldflags = common_flags
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 2ebac33..2b0d2c5 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -870,6 +870,10 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotificationHelper$OpenUrlReceiver"
+            android:exported="false"/>
+        <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotificationHelper$DeleteReceiver"
+            android:exported="false"/>
         <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotificationHelper$TimeoutReceiver"
             android:exported="false"/>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
index 337a3a9..a149fca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
@@ -11,6 +11,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
@@ -20,6 +21,8 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.ShortcutHelper;
@@ -30,13 +33,42 @@
  *
  * Exposes helper functions to native C++ code.
  */
+@JNINamespace("ntp_snippets")
 public class ContentSuggestionsNotificationHelper {
     private static final String NOTIFICATION_TAG = "ContentSuggestionsNotification";
     private static final String NOTIFICATION_ID_EXTRA = "notification_id";
 
+    private static final String PREF_CACHED_ACTION_TAP =
+            "ntp.content_suggestions.notification.cached_action_tap";
+    private static final String PREF_CACHED_ACTION_DISMISSAL =
+            "ntp.content_suggestions.notification.cached_action_dismissal";
+    private static final String PREF_CACHED_ACTION_HIDE_DEADLINE =
+            "ntp.content_suggestions.notification.cached_action_hide_deadline";
+    private static final String PREF_CACHED_CONSECUTIVE_IGNORED =
+            "ntp.content_suggestions.notification.cached_consecutive_ignored";
+
     private ContentSuggestionsNotificationHelper() {} // Prevent instantiation
 
     /**
+     * Opens the content suggestion when notification is tapped.
+     */
+    public static final class OpenUrlReceiver extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            openUrl(intent.getData());
+            recordCachedActionMetric(PREF_CACHED_ACTION_TAP);
+        }
+    }
+
+    /**
+     * Records dismissal when notification is swiped away.
+     */
+    public static final class DeleteReceiver extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            recordCachedActionMetric(PREF_CACHED_ACTION_DISMISSAL);
+        }
+    }
+
+    /**
      * Removes the notification after a timeout period.
      */
     public static final class TimeoutReceiver extends BroadcastReceiver {
@@ -44,19 +76,19 @@
             int id = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1);
             if (id < 0) return;
             hideNotification(id);
+            recordCachedActionMetric(PREF_CACHED_ACTION_HIDE_DEADLINE);
         }
     }
 
-    @CalledByNative
-    private static void openUrl(String url) {
+    private static void openUrl(Uri uri) {
         Context context = ContextUtils.getApplicationContext();
-        Intent intent = new Intent();
-        intent.setAction(Intent.ACTION_VIEW);
-        intent.setData(Uri.parse(url));
-        intent.setClass(context, ChromeLauncherActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
-        intent.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
+        Intent intent = new Intent()
+                                .setAction(Intent.ACTION_VIEW)
+                                .setData(uri)
+                                .setClass(context, ChromeLauncherActivity.class)
+                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                                .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())
+                                .putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
         IntentHandler.addTrustedIntentExtras(intent);
         context.startActivity(intent);
     }
@@ -81,16 +113,13 @@
             }
         }
 
-        Intent intent = new Intent()
-                                .setAction(Intent.ACTION_VIEW)
-                                .setData(Uri.parse(url))
-                                .setClass(context, ChromeLauncherActivity.class)
-                                .putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true)
-                                .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+        Intent contentIntent = new Intent(context, OpenUrlReceiver.class).setData(Uri.parse(url));
+        Intent deleteIntent = new Intent(context, DeleteReceiver.class).setData(Uri.parse(url));
         NotificationCompat.Builder builder =
                 new NotificationCompat.Builder(context)
                         .setAutoCancel(true)
-                        .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
+                        .setContentIntent(PendingIntent.getBroadcast(context, 0, contentIntent, 0))
+                        .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0))
                         .setContentTitle(title)
                         .setContentText(text)
                         .setGroup(NOTIFICATION_TAG)
@@ -134,4 +163,65 @@
             manager.cancel(NOTIFICATION_TAG, 0);
         }
     }
+
+    /**
+     * Records that an action was performed on a notification.
+     *
+     * Also tracks the number of consecutively-ignored notifications, resetting it on a tap or
+     * otherwise incrementing it.
+     *
+     * This method may be called when the native library is not loaded. If it is loaded, the metrics
+     * will immediately be sent to C++. If not, it will cache them for a later call to
+     * flushCachedMetrics().
+     *
+     * @param prefName The name of the action metric pref to update (<tt>PREF_CACHED_ACTION_*</tt>)
+     */
+    private static void recordCachedActionMetric(String prefName) {
+        assert prefName.startsWith("ntp.content_suggestions.notification.cached_action_");
+
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        int currentValue = prefs.getInt(prefName, 0);
+        int consecutiveIgnored = 0;
+        if (!prefName.equals(PREF_CACHED_ACTION_TAP)) {
+            consecutiveIgnored = 1 + prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
+        }
+        prefs.edit()
+                .putInt(prefName, currentValue + 1)
+                .putInt(PREF_CACHED_CONSECUTIVE_IGNORED, consecutiveIgnored)
+                .apply();
+
+        if (LibraryLoader.isInitialized()) {
+            flushCachedMetrics();
+        }
+    }
+
+    /**
+     * Invokes nativeReceiveFlushedMetrics() with cached metrics and resets them.
+     *
+     * It may be called from either native or Java code, as long as the native libray is loaded.
+     */
+    @CalledByNative
+    private static void flushCachedMetrics() {
+        assert LibraryLoader.isInitialized();
+
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        int tapCount = prefs.getInt(PREF_CACHED_ACTION_TAP, 0);
+        int dismissalCount = prefs.getInt(PREF_CACHED_ACTION_DISMISSAL, 0);
+        int hideDeadlineCount = prefs.getInt(PREF_CACHED_ACTION_HIDE_DEADLINE, 0);
+        int consecutiveIgnored = prefs.getInt(PREF_CACHED_CONSECUTIVE_IGNORED, 0);
+
+        if (tapCount > 0 || dismissalCount > 0 || hideDeadlineCount > 0) {
+            nativeReceiveFlushedMetrics(tapCount, dismissalCount, hideDeadlineCount,
+                                        consecutiveIgnored);
+            prefs.edit()
+                    .remove(PREF_CACHED_ACTION_TAP)
+                    .remove(PREF_CACHED_ACTION_DISMISSAL)
+                    .remove(PREF_CACHED_ACTION_HIDE_DEADLINE)
+                    .remove(PREF_CACHED_CONSECUTIVE_IGNORED)
+                    .apply();
+        }
+    }
+
+    private static native void nativeReceiveFlushedMetrics(
+            int tapCount, int dismissalCount, int hideDeadlineCount, int consecutiveIgnored);
 }
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index b658c7e..b13781d9 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -197,6 +197,8 @@
       // Temporary for https://crbug.com/668633.
       {"swdh_set_hosted_version_worker_pid", crash_keys::kSmallSize},
       {"swdh_set_hosted_version_host_pid", crash_keys::kSmallSize},
+      {"swdh_set_hosted_version_is_new_process", crash_keys::kSmallSize},
+      {"swdh_set_hosted_version_restart_count", crash_keys::kSmallSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 0c5824d8..80d4e578 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7576,7 +7576,7 @@
         Passwords
       </message>
       <message name="IDS_PASSWORDS_AUTO_SIGNIN_TITLE" desc="Title for 'Auto sign-in' checkbox in the password dialog">
-        Auto Sign-in
+        Auto sign-in
       </message>
       <message name="IDS_PASSWORDS_AUTO_SIGNIN_DESCRIPTION" desc="Text under 'Auto sign-in' checkbox">
         Automatically sign in to websites using stored credentials. When the feature is disabled, you will be asked for confirmation every time before signing in to a website.
diff --git a/chrome/app_shim/chrome_main_app_mode_mac.mm b/chrome/app_shim/chrome_main_app_mode_mac.mm
index 9d2b483..33be302 100644
--- a/chrome/app_shim/chrome_main_app_mode_mac.mm
+++ b/chrome/app_shim/chrome_main_app_mode_mac.mm
@@ -641,7 +641,9 @@
   g_io_thread = io_thread;
 
   mojo::edk::Init();
-  mojo::edk::ScopedIPCSupport ipc_support(io_thread->task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      io_thread->task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
 
   // Find already running instances of Chrome.
   pid_t pid = -1;
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 25118bc..b847498 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -746,6 +746,8 @@
     "ntp_snippets/download_suggestions_provider.h",
     "ntp_snippets/ntp_snippets_features.cc",
     "ntp_snippets/ntp_snippets_features.h",
+    "ntp_snippets/ntp_snippets_metrics.cc",
+    "ntp_snippets/ntp_snippets_metrics.h",
     "ntp_tiles/chrome_most_visited_sites_factory.cc",
     "ntp_tiles/chrome_most_visited_sites_factory.h",
     "ntp_tiles/chrome_popular_sites_factory.cc",
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 8a18442..d09a583 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/android/metrics/uma_utils.h"
 #include "chrome/browser/android/metrics/variations_session.h"
 #include "chrome/browser/android/net/external_estimate_provider_android.h"
+#include "chrome/browser/android/ntp/content_suggestions_notification_helper.h"
 #include "chrome/browser/android/ntp/most_visited_sites_bridge.h"
 #include "chrome/browser/android/ntp/ntp_snippets_bridge.h"
 #include "chrome/browser/android/ntp/recent_tabs_page_prefs.h"
@@ -260,6 +261,8 @@
      ConnectionInfoPopupAndroid::RegisterConnectionInfoPopupAndroid},
     {"SecurityStateModel", RegisterSecurityStateModelAndroid},
     {"ConnectivityChecker", chrome::android::RegisterConnectivityChecker},
+    {"ContentSuggestionsNotificationHelper",
+     ntp_snippets::ContentSuggestionsNotificationHelper::Register},
     {"ContextMenuHelper", RegisterContextMenuHelper},
     {"ContextualSearchManager", RegisterContextualSearchManager},
     {"ContextualSearchSceneLayer", RegisterContextualSearchSceneLayer},
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
index db5abdd3..ccfdc9e 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
@@ -8,20 +8,39 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ntp_snippets/ntp_snippets_features.h"
+#include "chrome/browser/ntp_snippets/ntp_snippets_metrics.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
 #include "jni/ContentSuggestionsNotificationHelper_jni.h"
 #include "ui/gfx/android/java_bitmap.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
 
+using base::android::JavaParamRef;
+
 namespace ntp_snippets {
 
-void ContentSuggestionsNotificationHelper::OpenURL(const GURL& url) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ContentSuggestionsNotificationHelper_openUrl(
-      env, base::android::ConvertUTF8ToJavaString(env, url.spec()));
+namespace {
+
+bool IsDisabledForProfile(Profile* profile) {
+  PrefService* prefs = profile->GetPrefs();
+  int current =
+      prefs->GetInteger(prefs::kContentSuggestionsConsecutiveIgnoredPrefName);
+  int limit = variations::GetVariationParamByFeatureAsInt(
+      kContentSuggestionsNotificationsFeature,
+      kContentSuggestionsNotificationsIgnoredLimitParam,
+      kContentSuggestionsNotificationsIgnoredDefaultLimit);
+  return current >= limit;
 }
 
+}  // namespace
+
 void ContentSuggestionsNotificationHelper::SendNotification(
     const GURL& url,
     const base::string16& title,
@@ -50,4 +69,59 @@
   Java_ContentSuggestionsNotificationHelper_hideAllNotifications(env);
 }
 
+void ContentSuggestionsNotificationHelper::FlushCachedMetrics() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_ContentSuggestionsNotificationHelper_flushCachedMetrics(env);
+}
+
+bool ContentSuggestionsNotificationHelper::IsDisabledForProfile(
+    Profile* profile) {
+  return ntp_snippets::IsDisabledForProfile(profile);
+}
+
+// static
+bool ContentSuggestionsNotificationHelper::Register(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+static void ReceiveFlushedMetrics(JNIEnv* env,
+                                  const JavaParamRef<jclass>& class_object,
+                                  jint tap_count,
+                                  jint dismissal_count,
+                                  jint hide_deadline_count,
+                                  jint consecutive_ignored) {
+  DVLOG(1) << "Flushing metrics: tap_count=" << tap_count
+           << "; dismissal_count=" << dismissal_count
+           << "; hide_deadline_count=" << hide_deadline_count
+           << "; consecutive_ignored=" << consecutive_ignored;
+  Profile* profile = ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
+  PrefService* prefs = profile->GetPrefs();
+
+  for (int i = 0; i < tap_count; ++i) {
+    RecordContentSuggestionsNotificationAction(CONTENT_SUGGESTIONS_TAP);
+  }
+  for (int i = 0; i < dismissal_count; ++i) {
+    RecordContentSuggestionsNotificationAction(CONTENT_SUGGESTIONS_DISMISSAL);
+  }
+  for (int i = 0; i < hide_deadline_count; ++i) {
+    RecordContentSuggestionsNotificationAction(
+        CONTENT_SUGGESTIONS_HIDE_DEADLINE);
+  }
+
+  const bool was_disabled = IsDisabledForProfile(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
+    // with the number of ignored notifications since that point.
+    consecutive_ignored +=
+        prefs->GetInteger(prefs::kContentSuggestionsConsecutiveIgnoredPrefName);
+  }
+  prefs->SetInteger(prefs::kContentSuggestionsConsecutiveIgnoredPrefName,
+                    consecutive_ignored);
+  const bool is_disabled = IsDisabledForProfile(profile);
+  if (!was_disabled && is_disabled) {
+    RecordContentSuggestionsNotificationOptOut(CONTENT_SUGGESTIONS_IMPLICIT);
+  }
+}
+
 }  // namespace ntp_snippets
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.h b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
index e9c7d93c..05f54b1e 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.h
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ANDROID_NTP_CONTENT_SUGGESTIONS_NOTIFICATION_HELPER_H_
 #define CHROME_BROWSER_ANDROID_NTP_CONTENT_SUGGESTIONS_NOTIFICATION_HELPER_H_
 
+#include <jni.h>
 #include <string>
 
 #include "base/macros.h"
@@ -12,6 +13,8 @@
 #include "base/time/time.h"
 #include "url/gurl.h"
 
+class Profile;
+
 namespace gfx {
 class Image;
 }  // namespace gfx
@@ -20,7 +23,6 @@
 
 class ContentSuggestionsNotificationHelper {
  public:
-  static void OpenURL(const GURL& url);
   static void SendNotification(const GURL& url,
                                const base::string16& title,
                                const base::string16& text,
@@ -28,6 +30,22 @@
                                base::Time timeout_at);
   static void HideAllNotifications();
 
+  // Moves metrics tracked in Java into native histograms. Should be called when
+  // the native library starts up, to capture any actions that were taken since
+  // the last time it was running. (Harmless to call more often, though)
+  //
+  // Also updates the "consecutive ignored" preference, which is computed from
+  // the actions taken on notifications, and maybe the "opt outs" metric, which
+  // 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);
+
+  // Registers JNI methods.
+  static bool Register(JNIEnv* env);
+
  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 cb7bfab..56a6bb5 100644
--- a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
@@ -12,7 +12,9 @@
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/browser/ntp_snippets/ntp_snippets_features.h"
+#include "chrome/browser/ntp_snippets/ntp_snippets_metrics.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -68,13 +70,16 @@
                     Profile* profile,
                     PrefService* prefs)
       : service_(service),
+        profile_(profile),
         prefs_(prefs),
         app_status_listener_(base::Bind(&NotifyingObserver::AppStatusChanged,
                                         base::Unretained(this))),
         weak_ptr_factory_(this) {}
 
   void OnNewSuggestions(Category category) override {
-    if (!ShouldNotifyInState(app_status_listener_.GetState())) {
+    if (!ShouldNotifyInState(app_status_listener_.GetState()) ||
+        ContentSuggestionsNotificationHelper::IsDisabledForProfile(profile_)) {
+      DVLOG(1) << "notification suppressed";
       return;
     }
     const ContentSuggestion* suggestion = GetSuggestionToNotifyAbout(category);
@@ -108,25 +113,33 @@
       case CategoryStatus::NOT_PROVIDED:
       case CategoryStatus::SIGNED_OUT:
         ContentSuggestionsNotificationHelper::HideAllNotifications();
+        RecordContentSuggestionsNotificationAction(
+            CONTENT_SUGGESTIONS_HIDE_DISABLED);
         break;
     }
   }
 
   void OnSuggestionInvalidated(
       const ContentSuggestion::ID& suggestion_id) override {
+    // TODO(sfiera): handle concurrent notifications and non-articles properly.
     if (suggestion_id.category().IsKnownCategory(KnownCategories::ARTICLES) &&
         (suggestion_id.id_within_category() ==
          prefs_->GetString(kNotificationIDWithinCategory))) {
       ContentSuggestionsNotificationHelper::HideAllNotifications();
+      RecordContentSuggestionsNotificationAction(
+          CONTENT_SUGGESTIONS_HIDE_EXPIRY);
     }
   }
 
   void OnFullRefreshRequired() override {
     ContentSuggestionsNotificationHelper::HideAllNotifications();
+    RecordContentSuggestionsNotificationAction(CONTENT_SUGGESTIONS_HIDE_EXPIRY);
   }
 
   void ContentSuggestionsServiceShutdown() override {
     ContentSuggestionsNotificationHelper::HideAllNotifications();
+    RecordContentSuggestionsNotificationAction(
+        CONTENT_SUGGESTIONS_HIDE_SHUTDOWN);
   }
 
  private:
@@ -154,6 +167,8 @@
   void AppStatusChanged(base::android::ApplicationState state) {
     if (!ShouldNotifyInState(state)) {
       ContentSuggestionsNotificationHelper::HideAllNotifications();
+      RecordContentSuggestionsNotificationAction(
+          CONTENT_SUGGESTIONS_HIDE_FRONTMOST);
     }
   }
 
@@ -172,9 +187,14 @@
     prefs_->SetString(kNotificationIDWithinCategory, id.id_within_category());
     ContentSuggestionsNotificationHelper::SendNotification(
         url, title, publisher, CropSquare(image), timeout_at);
+    RecordContentSuggestionsNotificationImpression(
+        id.category().IsKnownCategory(KnownCategories::ARTICLES)
+            ? CONTENT_SUGGESTIONS_ARTICLE
+            : CONTENT_SUGGESTIONS_NONARTICLE);
   }
 
   ContentSuggestionsService* const service_;
+  Profile* const profile_;
   PrefService* const prefs_;
   base::android::ApplicationStatusListener app_status_listener_;
 
@@ -190,6 +210,7 @@
     : observer_(base::MakeUnique<NotifyingObserver>(suggestions,
                                                     profile,
                                                     profile->GetPrefs())) {
+  ContentSuggestionsNotificationHelper::FlushCachedMetrics();
   suggestions->AddObserver(observer_.get());
 }
 
@@ -199,4 +220,6 @@
 void ContentSuggestionsNotifierService::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterStringPref(kNotificationIDWithinCategory, std::string());
+  registry->RegisterIntegerPref(
+      prefs::kContentSuggestionsConsecutiveIgnoredPrefName, 0);
 }
diff --git a/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.cc b/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.cc
index 9cd7a6e..4374579 100644
--- a/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.cc
+++ b/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.cc
@@ -12,21 +12,22 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/android/search_geolocation/search_geolocation_disclosure_infobar_delegate.h"
-#include "chrome/browser/permissions/permission_manager.h"
+#include "chrome/browser/android/search_geolocation/search_geolocation_service.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/search_engines/template_url.h"
-#include "components/search_engines/template_url_service.h"
 #include "components/variations/variations_associated_data.h"
-#include "content/public/browser/permission_type.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/GeolocationHeader_jni.h"
 #include "jni/SearchGeolocationDisclosureTabHelper_jni.h"
 #include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h"
+#include "url/origin.h"
 
 namespace {
 
@@ -77,10 +78,69 @@
 
 void SearchGeolocationDisclosureTabHelper::NavigationEntryCommitted(
     const content::LoadCommittedDetails& load_details) {
-  if (consistent_geolocation_enabled_) {
-    MaybeShowDefaultSearchGeolocationDisclosure(
-        web_contents()->GetVisibleURL());
+  if (consistent_geolocation_enabled_)
+    MaybeShowDisclosure(web_contents()->GetVisibleURL());
+}
+
+void SearchGeolocationDisclosureTabHelper::MaybeShowDisclosure(
+    const GURL& gurl) {
+  if (!ShouldShowDisclosureForUrl(gurl))
+    return;
+
+  // Check that the Chrome app has geolocation permission.
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (!Java_GeolocationHeader_hasGeolocationPermission(env))
+    return;
+
+  // Don't show the infobar if the user has dismissed it, or they've seen it
+  // enough times already.
+  PrefService* prefs = GetProfile()->GetPrefs();
+  bool dismissed_already =
+      prefs->GetBoolean(prefs::kSearchGeolocationDisclosureDismissed);
+  int shown_count =
+      prefs->GetInteger(prefs::kSearchGeolocationDisclosureShownCount);
+  if (dismissed_already || shown_count >= GetMaxShowCount()) {
+    // Record metrics for the state of permissions after the disclosure has been
+    // shown. This is not done immediately after showing the last disclosure
+    // (i.e. at the end of this function), but on the next omnibox search, to
+    // allow the metric to capture changes to settings done by the user as a
+    // result of clicking on the Settings link in the disclosure.
+    RecordPostDisclosureMetrics(gurl);
+    return;
   }
+
+  // Or if it has been shown too recently.
+  base::Time last_shown = base::Time::FromInternalValue(
+      prefs->GetInt64(prefs::kSearchGeolocationDisclosureLastShowDate));
+  if (GetTimeNow() - last_shown < base::TimeDelta::FromDays(GetDaysPerShow())) {
+    return;
+  }
+
+  // Record metrics for the state of permissions before the disclosure has been
+  // shown.
+  RecordPreDisclosureMetrics(gurl);
+
+  // Only show the disclosure if the geolocation permission is set ask (i.e. has
+  // not been explicitly set or revoked).
+  ContentSetting status =
+      HostContentSettingsMapFactory::GetForProfile(GetProfile())
+          ->GetContentSetting(gurl, gurl, CONTENT_SETTINGS_TYPE_GEOLOCATION,
+                              std::string());
+  if (status != CONTENT_SETTING_ASK)
+    return;
+
+  // And only show disclosure if the DSE geolocation setting is on.
+  SearchGeolocationService* service =
+      SearchGeolocationService::Factory::GetForBrowserContext(GetProfile());
+  if (!service->GetDSEGeolocationSetting())
+    return;
+
+  // All good, let's show the disclosure and increment the shown count.
+  SearchGeolocationDisclosureInfoBarDelegate::Create(web_contents(), gurl);
+  shown_count++;
+  prefs->SetInteger(prefs::kSearchGeolocationDisclosureShownCount, shown_count);
+  prefs->SetInt64(prefs::kSearchGeolocationDisclosureLastShowDate,
+                  GetTimeNow().ToInternalValue());
 }
 
 // static
@@ -111,89 +171,20 @@
   return RegisterNativesImpl(env);
 }
 
-void SearchGeolocationDisclosureTabHelper::
-    MaybeShowDefaultSearchGeolocationDisclosure(const GURL& gurl) {
-  // Don't show in incognito.
-  if (GetProfile()->IsOffTheRecord())
-    return;
-
-  if (!ShouldShowDisclosureForUrl(gurl))
-    return;
-
-  // Don't show the infobar if the user has dismissed it, or they've seen it
-  // enough times already.
-  PrefService* prefs = GetProfile()->GetPrefs();
-  bool dismissed_already =
-      prefs->GetBoolean(prefs::kSearchGeolocationDisclosureDismissed);
-  int shown_count =
-      prefs->GetInteger(prefs::kSearchGeolocationDisclosureShownCount);
-  if (dismissed_already || shown_count >= GetMaxShowCount()) {
-    // Record metrics for the state of permissions after the disclosure has been
-    // shown. This is not done immediately after showing the last disclosure
-    // (i.e. at the end of this function), but on the next omnibox search, to
-    // allow the metric to capture changes to settings done by the user as a
-    // result of clicking on the Settings link in the disclosure.
-    RecordPostDisclosureMetrics(gurl);
-    return;
-  }
-
-  // Or if it has been shown too recently.
-  base::Time last_shown = base::Time::FromInternalValue(
-      prefs->GetInt64(prefs::kSearchGeolocationDisclosureLastShowDate));
-  if (GetTimeNow() - last_shown < base::TimeDelta::FromDays(GetDaysPerShow())) {
-    return;
-  }
-
-  // Check that the Chrome app has geolocation permission.
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!Java_GeolocationHeader_hasGeolocationPermission(env))
-    return;
-
-  // Record metrics for the state of permissions before the disclosure has been
-  // shown.
-  RecordPreDisclosureMetrics(gurl);
-
-  // Only show the disclosure if the geolocation permission is set to ASK
-  // (i.e. has not been explicitly set or revoked).
-  blink::mojom::PermissionStatus status =
-      PermissionManager::Get(GetProfile())
-          ->GetPermissionStatus(content::PermissionType::GEOLOCATION, gurl,
-                                gurl);
-  if (status != blink::mojom::PermissionStatus::ASK)
-    return;
-
-  // All good, let's show the disclosure and increment the shown count.
-  SearchGeolocationDisclosureInfoBarDelegate::Create(web_contents(), gurl);
-  shown_count++;
-  prefs->SetInteger(prefs::kSearchGeolocationDisclosureShownCount, shown_count);
-  prefs->SetInt64(prefs::kSearchGeolocationDisclosureLastShowDate,
-                  GetTimeNow().ToInternalValue());
-}
-
 bool SearchGeolocationDisclosureTabHelper::ShouldShowDisclosureForUrl(
     const GURL& gurl) {
+  SearchGeolocationService* service =
+      SearchGeolocationService::Factory::GetForBrowserContext(GetProfile());
+
+  // Check the service first, as we don't want to show the infobar even when
+  // testing if it does not exist.
+  if (!service)
+    return false;
+
   if (gIgnoreUrlChecksForTesting)
     return true;
 
-  // Only show the disclosure for default search navigations from the omnibox.
-  TemplateURLService* template_url_service =
-      TemplateURLServiceFactory::GetForProfile(GetProfile());
-  bool is_search_url =
-      template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
-          gurl);
-  if (!is_search_url)
-    return false;
-
-  // Only show the disclosure if Google is the default search engine.
-  TemplateURL* default_search =
-      template_url_service->GetDefaultSearchProvider();
-  if (!default_search ||
-      !default_search->url_ref().HasGoogleBaseURLs(
-          template_url_service->search_terms_data())) {
-    return false;
-  }
-
-  return true;
+  return service->UseDSEGeolocationSetting(url::Origin(gurl));
 }
 
 void SearchGeolocationDisclosureTabHelper::RecordPreDisclosureMetrics(
@@ -201,15 +192,15 @@
   PrefService* prefs = GetProfile()->GetPrefs();
   if (!prefs->GetBoolean(
           prefs::kSearchGeolocationPreDisclosureMetricsRecorded)) {
-    blink::mojom::PermissionStatus status =
-        PermissionManager::Get(GetProfile())
-            ->GetPermissionStatus(content::PermissionType::GEOLOCATION, gurl,
-                                  gurl);
-    UMA_HISTOGRAM_ENUMERATION("GeolocationDisclosure.PreDisclosurePermission",
-                              static_cast<base::HistogramBase::Sample>(status),
-                              static_cast<base::HistogramBase::Sample>(
-                                  blink::mojom::PermissionStatus::LAST) +
-                                  1);
+    ContentSetting status =
+        HostContentSettingsMapFactory::GetForProfile(GetProfile())
+            ->GetContentSetting(gurl, gurl, CONTENT_SETTINGS_TYPE_GEOLOCATION,
+                                std::string());
+    UMA_HISTOGRAM_ENUMERATION(
+        "GeolocationDisclosure.PreDisclosureContentSetting",
+        static_cast<base::HistogramBase::Sample>(status),
+        static_cast<base::HistogramBase::Sample>(CONTENT_SETTING_NUM_SETTINGS) +
+            1);
     prefs->SetBoolean(prefs::kSearchGeolocationPreDisclosureMetricsRecorded,
                       true);
   }
@@ -220,15 +211,15 @@
   PrefService* prefs = GetProfile()->GetPrefs();
   if (!prefs->GetBoolean(
           prefs::kSearchGeolocationPostDisclosureMetricsRecorded)) {
-    blink::mojom::PermissionStatus status =
-        PermissionManager::Get(GetProfile())
-            ->GetPermissionStatus(content::PermissionType::GEOLOCATION, gurl,
-                                  gurl);
-    UMA_HISTOGRAM_ENUMERATION("GeolocationDisclosure.PostDisclosurePermission",
-                              static_cast<base::HistogramBase::Sample>(status),
-                              static_cast<base::HistogramBase::Sample>(
-                                  blink::mojom::PermissionStatus::LAST) +
-                                  1);
+    ContentSetting status =
+        HostContentSettingsMapFactory::GetForProfile(GetProfile())
+            ->GetContentSetting(gurl, gurl, CONTENT_SETTINGS_TYPE_GEOLOCATION,
+                                std::string());
+    UMA_HISTOGRAM_ENUMERATION(
+        "GeolocationDisclosure.PostDisclosureContentSetting",
+        static_cast<base::HistogramBase::Sample>(status),
+        static_cast<base::HistogramBase::Sample>(CONTENT_SETTING_NUM_SETTINGS) +
+            1);
     prefs->SetBoolean(prefs::kSearchGeolocationPostDisclosureMetricsRecorded,
                       true);
   }
diff --git a/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.h b/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.h
index 771e6228f..7b3607f 100644
--- a/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.h
+++ b/chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.h
@@ -30,6 +30,8 @@
   void NavigationEntryCommitted(
       const content::LoadCommittedDetails& load_details) override;
 
+  void MaybeShowDisclosure(const GURL& gurl);
+
   static void ResetDisclosure(Profile* profile);
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -42,8 +44,6 @@
   friend class content::WebContentsUserData<
       SearchGeolocationDisclosureTabHelper>;
 
-  void MaybeShowDefaultSearchGeolocationDisclosure(const GURL& gurl);
-
   bool ShouldShowDisclosureForUrl(const GURL& gurl);
 
   // Record metrics, once per client, of the permission state before and after
diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc
index dd86c235..d1b4381 100644
--- a/chrome/browser/certificate_manager_model.cc
+++ b/chrome/browser/certificate_manager_model.cc
@@ -212,12 +212,12 @@
   return rv;
 }
 
-int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module,
+int CertificateManagerModel::ImportFromPKCS12(PK11SlotInfo* slot_info,
                                               const std::string& data,
                                               const base::string16& password,
                                               bool is_extractable) {
-  int result = cert_db_->ImportFromPKCS12(module, data, password,
-                                          is_extractable, NULL);
+  int result = cert_db_->ImportFromPKCS12(slot_info, data,
+                                          password, is_extractable, NULL);
   if (result == net::OK)
     Refresh();
   return result;
diff --git a/chrome/browser/certificate_manager_model.h b/chrome/browser/certificate_manager_model.h
index 81e65e4..434ad2d 100644
--- a/chrome/browser/certificate_manager_model.h
+++ b/chrome/browser/certificate_manager_model.h
@@ -83,9 +83,9 @@
 
   // Import private keys and certificates from PKCS #12 encoded
   // |data|, using the given |password|. If |is_extractable| is false,
-  // mark the private key as unextractable from the module.
+  // mark the private key as unextractable from the slot.
   // Returns a net error code on failure.
-  int ImportFromPKCS12(net::CryptoModule* module, const std::string& data,
+  int ImportFromPKCS12(PK11SlotInfo* slot_info, const std::string& data,
                        const base::string16& password, bool is_extractable);
 
   // Import user certificate from DER encoded |data|.
diff --git a/chrome/browser/chromeos/file_manager/zip_file_creator.cc b/chrome/browser/chromeos/file_manager/zip_file_creator.cc
index 5a3a2f4..d5d66208 100644
--- a/chrome/browser/chromeos/file_manager/zip_file_creator.cc
+++ b/chrome/browser/chromeos/file_manager/zip_file_creator.cc
@@ -9,7 +9,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/message_loop/message_loop.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
 #include "chrome/common/chrome_utility_messages.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
@@ -22,7 +22,7 @@
 namespace {
 
 // Creates the destination zip file only if it does not already exist.
-base::File OpenFileHandleOnBlockingThreadPool(const base::FilePath& zip_path) {
+base::File OpenFileHandleAsync(const base::FilePath& zip_path) {
   return base::File(zip_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
 }
 
@@ -45,10 +45,9 @@
 void ZipFileCreator::Start() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  base::PostTaskAndReplyWithResult(
-      BrowserThread::GetBlockingPool(),
-      FROM_HERE,
-      base::Bind(&OpenFileHandleOnBlockingThreadPool, dest_file_),
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, base::TaskTraits().MayBlock(),
+      base::Bind(&OpenFileHandleAsync, dest_file_),
       base::Bind(&ZipFileCreator::OnOpenFileHandle, this));
 }
 
diff --git a/chrome/browser/chromeos/policy/device_active_directory_policy_manager.cc b/chrome/browser/chromeos/policy/device_active_directory_policy_manager.cc
index 57b376d..ecefddc 100644
--- a/chrome/browser/chromeos/policy/device_active_directory_policy_manager.cc
+++ b/chrome/browser/chromeos/policy/device_active_directory_policy_manager.cc
@@ -32,6 +32,8 @@
 
   // Does nothing if |store_| hasn't yet initialized.
   PublishPolicy();
+
+  RefreshPolicies();
 }
 
 void DeviceActiveDirectoryPolicyManager::Shutdown() {
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
index 544ff61..ec84452 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
@@ -31,6 +31,7 @@
 
 // CloudPolicyStore implementation for device policy on Chrome OS. Policy is
 // stored/loaded via D-Bus to/from session_manager.
+// TODO(tnagel): Either drop "Cloud" from the name or refactor.
 class DeviceCloudPolicyStoreChromeOS
     : public CloudPolicyStore,
       public chromeos::DeviceSettingsService::Observer {
diff --git a/chrome/browser/chromeos/policy/user_active_directory_policy_manager.cc b/chrome/browser/chromeos/policy/user_active_directory_policy_manager.cc
index 5f5aa8f..273caa1 100644
--- a/chrome/browser/chromeos/policy/user_active_directory_policy_manager.cc
+++ b/chrome/browser/chromeos/policy/user_active_directory_policy_manager.cc
@@ -35,6 +35,8 @@
 
   // Does nothing if |store_| hasn't yet initialized.
   PublishPolicy();
+
+  RefreshPolicies();
 }
 
 void UserActiveDirectoryPolicyManager::Shutdown() {
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
index 207a807..b426846 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
@@ -204,9 +204,8 @@
   // has been a credential error, or if the initial session creation was not
   // completed (the oauth_token_status is not set to valid by OAuth2LoginManager
   // until profile creation/session restore is complete).
-  // TODO(tnagel): Don't limit blocking to cloud managed users.
   const bool block_forever_for_policy =
-      !is_active_directory && !user_manager->IsLoggedInAsStub() &&
+      !user_manager->IsLoggedInAsStub() &&
       (user_manager->IsCurrentUserNew() ||
        user_manager->GetActiveUser()->force_online_signin() ||
        user_manager->GetActiveUser()->oauth_token_status() !=
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index f63d065..748fb3e 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -2258,7 +2258,7 @@
                : disable_reasons;
   }
 
-  if (FeatureSwitch::prompt_for_external_extensions()->IsEnabled()) {
+  if (extensions::ExternalInstallManager::IsPromptingEnabled()) {
     // External extensions are initially disabled. We prompt the user before
     // enabling them. Hosted apps are excepted because they are not dangerous
     // (they need to be launched by the user anyway). We also don't prompt for
diff --git a/chrome/browser/extensions/external_install_manager.cc b/chrome/browser/extensions/external_install_manager.cc
index 802627a..330b59e 100644
--- a/chrome/browser/extensions/external_install_manager.cc
+++ b/chrome/browser/extensions/external_install_manager.cc
@@ -12,12 +12,14 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/external_install_error.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/version_info/version_info.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/feature_switch.h"
+#include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_url_handlers.h"
 
@@ -84,6 +86,16 @@
 ExternalInstallManager::~ExternalInstallManager() {
 }
 
+
+bool ExternalInstallManager::IsPromptingEnabled() {
+  // Enable this feature on canary on mac.
+#if defined(OS_MACOSX) && defined(GOOGLE_CHROME_BUILD)
+  return GetCurrentChannel() <= version_info::Channel::CANARY;
+#else
+  return FeatureSwitch::prompt_for_external_extensions()->IsEnabled();
+#endif
+}
+
 void ExternalInstallManager::AddExternalInstallError(const Extension* extension,
                                                      bool is_new_profile) {
   // Error already exists or has been previously shown.
@@ -116,7 +128,7 @@
 
 void ExternalInstallManager::UpdateExternalExtensionAlert() {
   // If the feature is not enabled do nothing.
-  if (!FeatureSwitch::prompt_for_external_extensions()->IsEnabled())
+  if (!IsPromptingEnabled())
     return;
 
   // Look for any extensions that were disabled because of being unacknowledged
@@ -219,7 +231,7 @@
 
 bool ExternalInstallManager::IsUnacknowledgedExternalExtension(
     const Extension& extension) const {
-  if (!FeatureSwitch::prompt_for_external_extensions()->IsEnabled())
+  if (!IsPromptingEnabled())
     return false;
 
   int disable_reasons = extension_prefs_->GetDisableReasons(extension.id());
diff --git a/chrome/browser/extensions/external_install_manager.h b/chrome/browser/extensions/external_install_manager.h
index 99c60cb4..0f0b73d 100644
--- a/chrome/browser/extensions/external_install_manager.h
+++ b/chrome/browser/extensions/external_install_manager.h
@@ -34,6 +34,9 @@
                          bool is_first_run);
   ~ExternalInstallManager() override;
 
+  // Returns true if prompting for external extensions is enabled.
+  static bool IsPromptingEnabled();
+
   // Removes the error associated with a given extension.
   void RemoveExternalInstallError(const std::string& extension_id);
 
diff --git a/chrome/browser/geolocation/OWNERS b/chrome/browser/geolocation/OWNERS
index df3450e..59d7e30 100644
--- a/chrome/browser/geolocation/OWNERS
+++ b/chrome/browser/geolocation/OWNERS
@@ -1,2 +1,4 @@
+benwells@chromium.org
 mvanouwerkerk@chromium.org
+raymes@chromium.org
 timvolodine@chromium.org
diff --git a/chrome/browser/geolocation/geolocation_permission_context_android.cc b/chrome/browser/geolocation/geolocation_permission_context_android.cc
index 51f1f1b..40c1563 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_android.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_android.cc
@@ -10,12 +10,14 @@
 #include "base/bind.h"
 #include "chrome/browser/android/location_settings.h"
 #include "chrome/browser/android/location_settings_impl.h"
+#include "chrome/browser/android/search_geolocation/search_geolocation_disclosure_tab_helper.h"
 #include "chrome/browser/android/search_geolocation/search_geolocation_service.h"
 #include "chrome/browser/permissions/permission_request_id.h"
 #include "chrome/browser/permissions/permission_update_infobar_delegate_android.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/infobars/core/infobar.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
@@ -104,6 +106,33 @@
   GeolocationPermissionContext::CancelPermissionRequest(web_contents, id);
 }
 
+void GeolocationPermissionContextAndroid::NotifyPermissionSet(
+    const PermissionRequestID& id,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin,
+    const BrowserPermissionCallback& callback,
+    bool persist,
+    ContentSetting content_setting) {
+  GeolocationPermissionContext::NotifyPermissionSet(id, requesting_origin,
+                                                    embedding_origin, callback,
+                                                    persist, content_setting);
+
+  // If this is the default search origin, and the DSE Geolocation setting is
+  // being used, potentially show the disclosure.
+  if (requesting_origin == embedding_origin) {
+    content::WebContents* web_contents =
+        content::WebContents::FromRenderFrameHost(
+            content::RenderFrameHost::FromID(id.render_process_id(),
+                                             id.render_frame_id()));
+    SearchGeolocationDisclosureTabHelper* disclosure_helper =
+        SearchGeolocationDisclosureTabHelper::FromWebContents(web_contents);
+
+    // The tab helper can be null in tests.
+    if (disclosure_helper)
+      disclosure_helper->MaybeShowDisclosure(requesting_origin);
+  }
+}
+
 void GeolocationPermissionContextAndroid::HandleUpdateAndroidPermissions(
     const PermissionRequestID& id,
     const GURL& requesting_frame_origin,
diff --git a/chrome/browser/geolocation/geolocation_permission_context_android.h b/chrome/browser/geolocation/geolocation_permission_context_android.h
index b939a09..a739df6 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_android.h
+++ b/chrome/browser/geolocation/geolocation_permission_context_android.h
@@ -62,6 +62,12 @@
       const BrowserPermissionCallback& callback) override;
   void CancelPermissionRequest(content::WebContents* web_contents,
                                const PermissionRequestID& id) override;
+  void NotifyPermissionSet(const PermissionRequestID& id,
+                           const GURL& requesting_origin,
+                           const GURL& embedding_origin,
+                           const BrowserPermissionCallback& callback,
+                           bool persist,
+                           ContentSetting content_setting) override;
 
   void HandleUpdateAndroidPermissions(const PermissionRequestID& id,
                                       const GURL& requesting_frame_origin,
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index ea95288..352d037 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -107,7 +107,7 @@
  protected:
   IOThreadTestWithIOThreadObject()
       : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD |
-                       content::TestBrowserThreadBundle::DONT_START_THREADS) {
+                       content::TestBrowserThreadBundle::DONT_CREATE_THREADS) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     event_router_forwarder_ = new extensions::EventRouterForwarder;
 #endif
@@ -139,7 +139,7 @@
     // Now that IOThread object is registered starting the threads will
     // call the IOThread::Init(). This sets up the environment needed for
     // these tests.
-    thread_bundle_.Start();
+    thread_bundle_.CreateThreads();
   }
 
   ~IOThreadTestWithIOThreadObject() override {
diff --git a/chrome/browser/ntp_snippets/ntp_snippets_features.cc b/chrome/browser/ntp_snippets/ntp_snippets_features.cc
index 45b1a386..65b70ba 100644
--- a/chrome/browser/ntp_snippets/ntp_snippets_features.cc
+++ b/chrome/browser/ntp_snippets/ntp_snippets_features.cc
@@ -9,3 +9,5 @@
 
 const char kContentSuggestionsNotificationsAlwaysNotifyParam[] =
     "always_notify";
+const char kContentSuggestionsNotificationsIgnoredLimitParam[] =
+    "ignored_limit";
diff --git a/chrome/browser/ntp_snippets/ntp_snippets_features.h b/chrome/browser/ntp_snippets/ntp_snippets_features.h
index 02a33f6a..5e7817c 100644
--- a/chrome/browser/ntp_snippets/ntp_snippets_features.h
+++ b/chrome/browser/ntp_snippets/ntp_snippets_features.h
@@ -14,4 +14,9 @@
 // "true": always send a notification when we receive ARTICLES suggestions
 extern const char kContentSuggestionsNotificationsAlwaysNotifyParam[];
 
+// An integer. The number of notifications that can be ignored. If the user
+// ignores this many notifications or more, we stop sending them.
+extern const char kContentSuggestionsNotificationsIgnoredLimitParam[];
+constexpr int kContentSuggestionsNotificationsIgnoredDefaultLimit = 3;
+
 #endif  // CHROME_BROWSER_NTP_SNIPPETS_NTP_SNIPPETS_FEATURES_H_
diff --git a/chrome/browser/ntp_snippets/ntp_snippets_metrics.cc b/chrome/browser/ntp_snippets/ntp_snippets_metrics.cc
new file mode 100644
index 0000000..5084741c
--- /dev/null
+++ b/chrome/browser/ntp_snippets/ntp_snippets_metrics.cc
@@ -0,0 +1,36 @@
+// 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 "chrome/browser/ntp_snippets/ntp_snippets_metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace {
+
+const char kContentSuggestionsNotificationImpressions[] =
+    "NewTabPage.ContentSuggestions.Notifications.Impressions";
+const char kContentSuggestionsNotificationActions[] =
+    "NewTabPage.ContentSuggestions.Notifications.Actions";
+const char kContentSuggestionsNotificationOptOuts[] =
+    "NewTabPage.ContentSuggestions.Notifications.AutoOptOuts";
+
+}  // namespace
+
+void RecordContentSuggestionsNotificationImpression(
+    ContentSuggestionsNotificationImpression what) {
+  UMA_HISTOGRAM_ENUMERATION(kContentSuggestionsNotificationImpressions, what,
+                            MAX_CONTENT_SUGGESTIONS_NOTIFICATION_IMPRESSION);
+}
+
+void RecordContentSuggestionsNotificationAction(
+    ContentSuggestionsNotificationAction what) {
+  UMA_HISTOGRAM_ENUMERATION(kContentSuggestionsNotificationActions, what,
+                            MAX_CONTENT_SUGGESTIONS_NOTIFICATION_ACTION);
+}
+
+void RecordContentSuggestionsNotificationOptOut(
+    ContentSuggestionsNotificationOptOut what) {
+  UMA_HISTOGRAM_ENUMERATION(kContentSuggestionsNotificationOptOuts, what,
+                            MAX_CONTENT_SUGGESTIONS_NOTIFICATION_OPT_OUT);
+}
diff --git a/chrome/browser/ntp_snippets/ntp_snippets_metrics.h b/chrome/browser/ntp_snippets/ntp_snippets_metrics.h
new file mode 100644
index 0000000..de37765
--- /dev/null
+++ b/chrome/browser/ntp_snippets/ntp_snippets_metrics.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NTP_SNIPPETS_NTP_SNIPPETS_METRICS_H_
+#define CHROME_BROWSER_NTP_SNIPPETS_NTP_SNIPPETS_METRICS_H_
+
+enum ContentSuggestionsNotificationImpression {
+  CONTENT_SUGGESTIONS_ARTICLE = 0,  // Server-provided "articles" category.
+  CONTENT_SUGGESTIONS_NONARTICLE,   // Anything else.
+
+  MAX_CONTENT_SUGGESTIONS_NOTIFICATION_IMPRESSION
+};
+
+enum ContentSuggestionsNotificationAction {
+  CONTENT_SUGGESTIONS_TAP = 0,    // User tapped notification to open article.
+  CONTENT_SUGGESTIONS_DISMISSAL,  // User swiped notification to dismiss it.
+
+  CONTENT_SUGGESTIONS_HIDE_DEADLINE,   // notification_extra().deadline passed.
+  CONTENT_SUGGESTIONS_HIDE_EXPIRY,     // NTP no longer shows notified article.
+  CONTENT_SUGGESTIONS_HIDE_FRONTMOST,  // Chrome became the frontmost app.
+  CONTENT_SUGGESTIONS_HIDE_DISABLED,   // NTP no longer shows whole category.
+  CONTENT_SUGGESTIONS_HIDE_SHUTDOWN,   // Content sugg service is shutting down.
+
+  MAX_CONTENT_SUGGESTIONS_NOTIFICATION_ACTION
+};
+
+enum ContentSuggestionsNotificationOptOut {
+  CONTENT_SUGGESTIONS_IMPLICIT = 0,  // User ignored notifications.
+  CONTENT_SUGGESTIONS_EXPLICIT,      // User explicitly opted-out.
+
+  MAX_CONTENT_SUGGESTIONS_NOTIFICATION_OPT_OUT
+};
+
+void RecordContentSuggestionsNotificationImpression(
+    ContentSuggestionsNotificationImpression what);
+void RecordContentSuggestionsNotificationAction(
+    ContentSuggestionsNotificationAction what);
+void RecordContentSuggestionsNotificationOptOut(
+    ContentSuggestionsNotificationOptOut what);
+
+#endif  // CHROME_BROWSER_NTP_SNIPPETS_NTP_SNIPPETS_METRICS_H_
diff --git a/chrome/browser/password_manager/credential_manager_browsertest.cc b/chrome/browser/password_manager/credential_manager_browsertest.cc
index d10b3634..be572bb 100644
--- a/chrome/browser/password_manager/credential_manager_browsertest.cc
+++ b/chrome/browser/password_manager/credential_manager_browsertest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
-#include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -23,28 +22,6 @@
 
 namespace {
 
-// A helper class that synchronously waits until the password store handles a
-// GetLogins() request.
-class PasswordStoreResultsObserver
-    : public password_manager::PasswordStoreConsumer {
- public:
-  PasswordStoreResultsObserver() = default;
-
-  void OnGetPasswordStoreResults(
-      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override {
-    run_loop_.Quit();
-  }
-
-  void Wait() {
-    run_loop_.Run();
-  }
-
- private:
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordStoreResultsObserver);
-};
-
 class CredentialManagerBrowserTest : public PasswordManagerBrowserTestBase {
  public:
   CredentialManagerBrowserTest() = default;
@@ -54,17 +31,6 @@
         GetState() == password_manager::ui::CREDENTIAL_REQUEST_STATE;
   }
 
-  // Make sure that the password store processed all the previous calls which
-  // are executed on another thread.
-  void WaitForPasswordStore() {
-    scoped_refptr<password_manager::PasswordStore> password_store =
-        PasswordStoreFactory::GetForProfile(
-            browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS);
-    PasswordStoreResultsObserver syncer;
-    password_store->GetAutofillableLoginsWithAffiliatedRealms(&syncer);
-    syncer.Wait();
-  }
-
   // Similarly to PasswordManagerBrowserTestBase::NavigateToFile this is a
   // wrapper around ui_test_utils::NavigateURL that waits until DidFinishLoad()
   // fires. Different to NavigateToFile this method allows passing a test_server
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index a17d1f5..65af571 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -1412,6 +1412,81 @@
   EXPECT_FALSE(prompt_observer->IsShowingSavePrompt());
 }
 
+// Tests whether a attempted submission of a malicious credentials gets blocked.
+// This simulates a case which is described in http://crbug.com/571580.
+IN_PROC_BROWSER_TEST_F(
+    PasswordManagerBrowserTestBase,
+    NoPromptForSeperateLoginFormWhenSwitchingFromHttpsToHttp) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kAllowRunningInsecureContent);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kIgnoreCertificateErrors);
+  const base::FilePath::CharType kDocRoot[] =
+      FILE_PATH_LITERAL("chrome/test/data");
+  net::EmbeddedTestServer https_test_server(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_test_server.ServeFilesFromSourceDirectory(base::FilePath(kDocRoot));
+  ASSERT_TRUE(https_test_server.Start());
+
+  std::string path = "/password/password_form.html";
+  GURL https_url(https_test_server.GetURL(path));
+  ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme));
+
+  NavigationObserver form_observer(WebContents());
+  ui_test_utils::NavigateToURL(browser(), https_url);
+  form_observer.Wait();
+
+  std::string fill_and_submit_redirect =
+      "document.getElementById('username_redirect').value = 'user';"
+      "document.getElementById('password_redirect').value = 'password';"
+      "document.getElementById('submit_redirect').click()";
+  ASSERT_TRUE(
+      content::ExecuteScript(RenderViewHost(), fill_and_submit_redirect));
+
+  NavigationObserver redirect_observer(WebContents());
+  redirect_observer.SetPathToWaitFor("/password/redirect.html");
+  redirect_observer.Wait();
+
+  // Normally the redirect happens to done.html. Here an attack is simulated
+  // that hijacks the redirect to a attacker controlled page.
+  GURL http_url(
+      embedded_test_server()->GetURL("/password/simple_password.html"));
+  std::string attacker_redirect =
+      "window.location.href = '" + http_url.spec() + "';";
+  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), attacker_redirect));
+
+  NavigationObserver attacker_observer(WebContents());
+  attacker_observer.SetPathToWaitFor("/password/simple_password.html");
+  attacker_observer.Wait();
+
+  std::string fill_and_submit_attacker_form =
+      "document.getElementById('username_field').value = 'attacker_username';"
+      "document.getElementById('password_field').value = 'attacker_password';"
+      "document.getElementById('input_submit_button').click()";
+  ASSERT_TRUE(
+      content::ExecuteScript(RenderViewHost(), fill_and_submit_attacker_form));
+
+  NavigationObserver done_observer(WebContents());
+  done_observer.SetPathToWaitFor("/password/done.html");
+  done_observer.Wait();
+
+  WaitForPasswordStore();
+  BubbleObserver prompt_observer(WebContents());
+  EXPECT_TRUE(prompt_observer.IsShowingSavePrompt());
+  prompt_observer.AcceptSavePrompt();
+
+  // Wait for password store and check that credentials are stored.
+  WaitForPasswordStore();
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+  EXPECT_FALSE(password_store->IsEmpty());
+  CheckThatCredentialsStored(password_store.get(), base::ASCIIToUTF16("user"),
+                             base::ASCIIToUTF16("password"));
+}
+
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
                        PromptWhenPasswordFormWithoutUsernameFieldSubmitted) {
   scoped_refptr<password_manager::TestPasswordStore> password_store =
diff --git a/chrome/browser/password_manager/password_manager_test_base.cc b/chrome/browser/password_manager/password_manager_test_base.cc
index 44180f5..e8d0327 100644
--- a/chrome/browser/password_manager/password_manager_test_base.cc
+++ b/chrome/browser/password_manager/password_manager_test_base.cc
@@ -25,6 +25,30 @@
 #include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
+namespace {
+
+// A helper class that synchronously waits until the password store handles a
+// GetLogins() request.
+class PasswordStoreResultsObserver
+    : public password_manager::PasswordStoreConsumer {
+ public:
+  PasswordStoreResultsObserver() = default;
+
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override {
+    run_loop_.Quit();
+  }
+
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(PasswordStoreResultsObserver);
+};
+
+}  // namespace
+
 NavigationObserver::NavigationObserver(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       quit_on_entry_committed_(false),
@@ -230,6 +254,15 @@
       << ", expected_value = " << expected_value;
 }
 
+void PasswordManagerBrowserTestBase::WaitForPasswordStore() {
+  scoped_refptr<password_manager::PasswordStore> password_store =
+      PasswordStoreFactory::GetForProfile(browser()->profile(),
+                                          ServiceAccessType::IMPLICIT_ACCESS);
+  PasswordStoreResultsObserver syncer;
+  password_store->GetAutofillableLoginsWithAffiliatedRealms(&syncer);
+  syncer.Wait();
+}
+
 void PasswordManagerBrowserTestBase::CheckElementValue(
     const std::string& element_id,
     const std::string& expected_value) {
diff --git a/chrome/browser/password_manager/password_manager_test_base.h b/chrome/browser/password_manager/password_manager_test_base.h
index 5d36a1e..96180c9 100644
--- a/chrome/browser/password_manager/password_manager_test_base.h
+++ b/chrome/browser/password_manager/password_manager_test_base.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/test_utils.h"
 
@@ -122,6 +123,9 @@
   void WaitForElementValue(const std::string& iframe_id,
                            const std::string& element_id,
                            const std::string& expected_value);
+  // Make sure that the password store processed all the previous calls which
+  // are executed on another thread.
+  void WaitForPasswordStore();
   // Checks that the current "value" attribute of the HTML element with
   // |element_id| is equal to |expected_value|.
   void CheckElementValue(const std::string& element_id,
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 69d7fb1..65551c02 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -165,7 +165,7 @@
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchHistograms) {
   PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
   histogram_tester().ExpectTotalCount(
-      "Prerender.websame_NoStatePrefetchTTFCP.Warm", 0);
+      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 0);
 
   test_utils::FirstContentfulPaintManagerWaiter* fcp_waiter =
       test_utils::FirstContentfulPaintManagerWaiter::Create(
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
index 6a482e1..db561a30 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
@@ -264,7 +264,9 @@
 
   // Needed for IPC.
   mojo::edk::Init();
-  mojo::edk::ScopedIPCSupport ipc_support(service_process.io_task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      service_process.io_task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
 
   MockServiceIPCServer server(&service_process,
                               service_process.io_task_runner(),
@@ -520,7 +522,8 @@
 TEST_F(CloudPrintProxyPolicyStartupTest, StartAndShutdown) {
   mojo::edk::Init();
   mojo::edk::ScopedIPCSupport ipc_support(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
 
   TestingBrowserProcess* browser_process =
       TestingBrowserProcess::GetGlobal();
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index c89e97de..a947940 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -1307,14 +1307,13 @@
 // cert will be selected automatically, then a test which uses WebSocket runs.
 IN_PROC_BROWSER_TEST_F(SSLUITestWithClientCert, TestWSSClientCert) {
   // Import a client cert for test.
-  scoped_refptr<net::CryptoModule> crypt_module = cert_db_->GetPublicModule();
+  crypto::ScopedPK11Slot public_slot = cert_db_->GetPublicSlot();
   std::string pkcs12_data;
   base::FilePath cert_path = net::GetTestCertsDirectory().Append(
       FILE_PATH_LITERAL("websocket_client_cert.p12"));
   EXPECT_TRUE(base::ReadFileToString(cert_path, &pkcs12_data));
-  EXPECT_EQ(net::OK,
-            cert_db_->ImportFromPKCS12(
-                crypt_module.get(), pkcs12_data, base::string16(), true, NULL));
+  EXPECT_EQ(net::OK, cert_db_->ImportFromPKCS12(public_slot.get(), pkcs12_data,
+                                                base::string16(), true, NULL));
 
   // Start WebSocket test server with TLS and client cert authentication.
   net::SpawnedTestServer::SSLOptions options(
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
index bf7e29c3..95e5f53 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -857,7 +857,7 @@
   // for Chrome OS when the "Import and Bind" option is chosen.
   bool is_extractable = !use_hardware_backed_;
   int result = certificate_manager_model_->ImportFromPKCS12(
-      module_.get(), file_data_, password_, is_extractable);
+      module_->os_module_handle(), file_data_, password_, is_extractable);
   ImportExportCleanup();
   web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
   int string_id;
diff --git a/chrome/browser/ui/webui/settings/certificates_handler.cc b/chrome/browser/ui/webui/settings/certificates_handler.cc
index 16503d82..cee6591 100644
--- a/chrome/browser/ui/webui/settings/certificates_handler.cc
+++ b/chrome/browser/ui/webui/settings/certificates_handler.cc
@@ -740,7 +740,7 @@
   // for Chrome OS when the "Import and Bind" option is chosen.
   bool is_extractable = !use_hardware_backed_;
   int result = certificate_manager_model_->ImportFromPKCS12(
-      module_.get(), file_data_, password_, is_extractable);
+      module_->os_module_handle(), file_data_, password_, is_extractable);
   ImportExportCleanup();
   int string_id;
   switch (result) {
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index d759105..d85cdca 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -250,6 +250,8 @@
     // Temporary for https://crbug.com/668633.
     { "swdh_set_hosted_version_worker_pid", crash_keys::kSmallSize },
     { "swdh_set_hosted_version_host_pid", crash_keys::kSmallSize },
+    { "swdh_set_hosted_version_is_new_process", crash_keys::kSmallSize },
+    { "swdh_set_hosted_version_restart_count", crash_keys::kSmallSize },
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index e60041c..a510af5 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1543,6 +1543,11 @@
 
 // Keeps track of sync promo collapsed state in the Other Devices menu.
 const char kNtpCollapsedSyncPromo[] = "ntp.collapsed_sync_promo";
+
+// Tracks how many notifications the user has ignored, so we can tell when we
+// should stop showing them.
+const char kContentSuggestionsConsecutiveIgnoredPrefName[] =
+    "ntp.content_suggestions.notifications.consecutive_ignored";
 #endif  // defined(OS_ANDROID)
 
 // Which page should be visible on the new tab page v4
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 67f9145..f0b812c9 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -557,6 +557,7 @@
 extern const char kNtpCollapsedRecentlyClosedTabs[];
 extern const char kNtpCollapsedSnapshotDocument[];
 extern const char kNtpCollapsedSyncPromo[];
+extern const char kContentSuggestionsConsecutiveIgnoredPrefName[];
 #endif  // defined(OS_ANDROID)
 extern const char kNtpShownPage[];
 
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index 3e3b8d8..394bc255 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -171,8 +171,9 @@
 
   // Initialize Mojo early so things can use it.
   mojo::edk::Init();
-  mojo_ipc_support_.reset(
-      new mojo::edk::ScopedIPCSupport(io_thread_->task_runner()));
+  mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport(
+      io_thread_->task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST));
 
   request_context_getter_ = new ServiceURLRequestContextGetter();
 
diff --git a/chrome/test/base/mojo_test_connector.cc b/chrome/test/base/mojo_test_connector.cc
index 34135f4..2fb999e 100644
--- a/chrome/test/base/mojo_test_connector.cc
+++ b/chrome/test/base/mojo_test_connector.cc
@@ -16,7 +16,6 @@
 #include "content/public/test/test_launcher.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/process_delegate.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/catalog/store.h"
 #include "services/service_manager/public/cpp/connector.h"
diff --git a/chrome/test/base/run_all_unittests.cc b/chrome/test/base/run_all_unittests.cc
index 17d4e8c..8536390 100644
--- a/chrome/test/base/run_all_unittests.cc
+++ b/chrome/test/base/run_all_unittests.cc
@@ -8,13 +8,15 @@
 #include "base/test/test_io_thread.h"
 #include "chrome/test/base/chrome_unit_test_suite.h"
 #include "content/public/test/unittest_test_suite.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 
 int main(int argc, char **argv) {
   content::UnitTestTestSuite test_suite(new ChromeUnitTestSuite(argc, argv));
 
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
 
   return base::LaunchUnitTests(
       argc, argv, base::Bind(&content::UnitTestTestSuite::Run,
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc
index 991ced9..72db754 100644
--- a/chromecast/crash/cast_crash_keys.cc
+++ b/chromecast/crash/cast_crash_keys.cc
@@ -108,6 +108,8 @@
     // Temporary for https://crbug.com/668633.
     { "swdh_set_hosted_version_worker_pid", kSmallSize },
     { "swdh_set_hosted_version_host_pid", kSmallSize },
+    { "swdh_set_hosted_version_is_new_process", kSmallSize },
+    { "swdh_set_hosted_version_restart_count", kSmallSize },
   };
 
   return base::debug::InitCrashKeys(fixed_keys, arraysize(fixed_keys),
diff --git a/chromeos/dbus/auth_policy_client.cc b/chromeos/dbus/auth_policy_client.cc
index f3181c9b..43578e3 100644
--- a/chromeos/dbus/auth_policy_client.cc
+++ b/chromeos/dbus/auth_policy_client.cc
@@ -71,12 +71,11 @@
 
   void RefreshUserPolicy(const AccountId& account_id,
                          const RefreshPolicyCallback& callback) override {
+    DCHECK(account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY);
     dbus::MethodCall method_call(authpolicy::kAuthPolicyInterface,
                                  authpolicy::kAuthPolicyRefreshUserPolicy);
     dbus::MessageWriter writer(&method_call);
-    // TODO(tnagel): Switch to GUID once authpolicyd, session_manager and
-    // cryptohome support it, cf. https://crbug.com/677497.
-    writer.AppendString(account_id.GetUserEmail());
+    writer.AppendString(account_id.GetAccountIdKey());
     proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::Bind(&AuthPolicyClientImpl::HandleRefreshPolicyCallback,
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index e12ac310..f434dcd4 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -212,17 +212,17 @@
     scoped_refptr<CryptohomeAuthenticator> resolver,
     bool ephemeral,
     bool create_if_nonexistent) {
+  if (attempt->user_context.GetAccountId().GetAccountType() ==
+      AccountType::ACTIVE_DIRECTORY) {
+    cryptohome::SetGaiaIdMigrationStatusDone(
+        attempt->user_context.GetAccountId());
+  }
   const bool is_gaiaid_migration_started = switches::IsGaiaIdMigrationStarted();
   if (!is_gaiaid_migration_started) {
     UMACryptohomeMigrationToGaiaId(CryptohomeMigrationToGaiaId::NOT_STARTED);
     DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
     return;
   }
-  if (attempt->user_context.GetAccountId().GetAccountType() ==
-      AccountType::ACTIVE_DIRECTORY) {
-    cryptohome::SetGaiaIdMigrationStatusDone(
-        attempt->user_context.GetAccountId());
-  }
   const bool already_migrated = cryptohome::GetGaiaIdMigrationStatus(
       attempt->user_context.GetAccountId());
   const bool has_account_key =
diff --git a/chromeos/network/onc/onc_certificate_importer_impl.cc b/chromeos/network/onc/onc_certificate_importer_impl.cc
index 7ab59a0..1f04a696 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl.cc
+++ b/chromeos/network/onc/onc_certificate_importer_impl.cc
@@ -286,12 +286,12 @@
   crypto::ScopedPK11Slot private_slot(nssdb->GetPrivateSlot());
   if (!private_slot)
     return false;
-  scoped_refptr<net::CryptoModule> module(
-      net::CryptoModule::CreateFromHandle(private_slot.get()));
+
   net::CertificateList imported_certs;
 
-  int import_result = nssdb->ImportFromPKCS12(
-      module.get(), decoded_pkcs12, base::string16(), false, &imported_certs);
+  int import_result =
+      nssdb->ImportFromPKCS12(private_slot.get(), decoded_pkcs12,
+                              base::string16(), false, &imported_certs);
   if (import_result != net::OK) {
     std::string error_string = net::ErrorToString(import_result);
     LOG(ERROR) << "Unable to import client certificate, error: "
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 0941c60..93d1bc6 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -266,6 +266,8 @@
       return "Invalid form";
     case SavePasswordProgressLogger::STRING_SYNC_CREDENTIAL:
       return "Credential is used for syncing passwords";
+    case STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME:
+      return "Blocked password due to same origin but insecure scheme";
     case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM:
       return "provisionally_saved_form";
     case SavePasswordProgressLogger::STRING_IGNORE_POSSIBLE_USERNAMES:
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index e5aaa70..de8a082 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -90,6 +90,7 @@
     STRING_FORM_BLACKLISTED,
     STRING_INVALID_FORM,
     STRING_SYNC_CREDENTIAL,
+    STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME,
     STRING_PROVISIONALLY_SAVED_FORM,
     STRING_IGNORE_POSSIBLE_USERNAMES,
     STRING_ON_PASSWORD_FORMS_RENDERED_METHOD,
diff --git a/components/nacl/common/nacl_service.cc b/components/nacl/common/nacl_service.cc
index 3ce685d..fb19f6a8 100644
--- a/components/nacl/common/nacl_service.cc
+++ b/components/nacl/common/nacl_service.cc
@@ -101,8 +101,9 @@
 std::unique_ptr<service_manager::ServiceContext> CreateNaClServiceContext(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     mojo::ScopedMessagePipeHandle* ipc_channel) {
-  auto ipc_support =
-      base::MakeUnique<mojo::edk::ScopedIPCSupport>(std::move(io_task_runner));
+  auto ipc_support = base::MakeUnique<mojo::edk::ScopedIPCSupport>(
+      std::move(io_task_runner),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
   EstablishMojoConnection();
 
   IPC::mojom::ChannelBootstrapPtr bootstrap;
diff --git a/components/password_manager/core/browser/browser_save_password_progress_logger.cc b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
index f8f1e64..db8803d 100644
--- a/components/password_manager/core/browser/browser_save_password_progress_logger.cc
+++ b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
@@ -112,6 +112,19 @@
   SendLog(message);
 }
 
+void BrowserSavePasswordProgressLogger::LogSuccessiveOrigins(
+    StringID label,
+    const GURL& old_origin,
+    const GURL& new_origin) {
+  std::string message = GetStringFromID(label) + ": {\n";
+  message +=
+      GetStringFromID(STRING_ORIGIN) + ": " + ScrubURL(old_origin) + "\n";
+  message +=
+      GetStringFromID(STRING_ORIGIN) + ": " + ScrubURL(new_origin) + "\n";
+  message += "}";
+  SendLog(message);
+}
+
 std::string BrowserSavePasswordProgressLogger::FormStructureToFieldsLogString(
     const autofill::FormStructure& form_structure) {
   std::string result;
diff --git a/components/password_manager/core/browser/browser_save_password_progress_logger.h b/components/password_manager/core/browser/browser_save_password_progress_logger.h
index ae556a2..d5c94232 100644
--- a/components/password_manager/core/browser/browser_save_password_progress_logger.h
+++ b/components/password_manager/core/browser/browser_save_password_progress_logger.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
+#include "url/gurl.h"
 
 namespace autofill {
 class FormStructure;
@@ -35,6 +36,12 @@
   void LogFormStructure(StringID label, const autofill::FormStructure& form);
 
   // Browser-specific addition to the base class' Log* methods. The input is
+  // sanitized and passed to SendLog for display.
+  void LogSuccessiveOrigins(StringID label,
+                            const GURL& old_origin,
+                            const GURL& new_origin);
+
+  // Browser-specific addition to the base class' Log* methods. The input is
   // passed to SendLog for display.
   void LogString(StringID label, const std::string& s);
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 491c5a60..574e49a 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -273,6 +273,17 @@
     return;
   }
 
+  bool should_block = ShouldBlockPasswordForSameOriginButDifferentScheme(form);
+  metrics_util::LogShouldBlockPasswordForSameOriginButDifferentScheme(
+      should_block);
+  if (should_block) {
+    if (logger)
+      logger->LogSuccessiveOrigins(
+          Logger::STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME,
+          main_frame_url_.GetOrigin(), form.origin.GetOrigin());
+    return;
+  }
+
   auto matched_manager_it = pending_login_managers_.end();
   PasswordFormManager::MatchResultMask current_match_result =
       PasswordFormManager::RESULT_NO_MATCH;
@@ -562,6 +573,15 @@
   return true;
 }
 
+bool PasswordManager::ShouldBlockPasswordForSameOriginButDifferentScheme(
+    const PasswordForm& form) const {
+  const GURL& old_origin = main_frame_url_.GetOrigin();
+  const GURL& new_origin = form.origin.GetOrigin();
+  return old_origin.host_piece() == new_origin.host_piece() &&
+         old_origin.SchemeIsCryptographic() &&
+         !new_origin.SchemeIsCryptographic();
+}
+
 bool PasswordManager::ShouldPromptUserToSavePassword() const {
   return !client_->IsAutomaticPasswordSavingEnabled() &&
          (provisional_save_manager_->IsNewLogin() ||
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h
index 467f6a88..ccc1d89 100644
--- a/components/password_manager/core/browser/password_manager.h
+++ b/components/password_manager/core/browser/password_manager.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
@@ -183,6 +184,10 @@
 #endif
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(
+      PasswordManagerTest,
+      ShouldBlockPasswordForSameOriginButDifferentSchemeTest);
+
   enum ProvisionalSaveFailure {
     SAVING_DISABLED,
     EMPTY_PASSWORD,
@@ -210,6 +215,13 @@
   // non-blacklisted.
   bool CanProvisionalManagerSave();
 
+  // Returns true if there already exists a provisionally saved password form
+  // from the same origin as |form|, but with a different and secure scheme.
+  // This prevents a potential attack where users can be tricked into saving
+  // unwanted credentials, see http://crbug.com/571580 for details.
+  bool ShouldBlockPasswordForSameOriginButDifferentScheme(
+      const autofill::PasswordForm& form) const;
+
   // Returns true if the user needs to be prompted before a password can be
   // saved (instead of automatically saving
   // the password), based on inspecting the state of
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index 7b55d8d..8bc566bc 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -104,6 +104,12 @@
                             CHROME_SIGNIN_ACTION_COUNT);
 }
 
+void LogShouldBlockPasswordForSameOriginButDifferentScheme(bool should_block) {
+  UMA_HISTOGRAM_BOOLEAN(
+      "PasswordManager.ShouldBlockPasswordForSameOriginButDifferentScheme",
+      should_block);
+}
+
 void LogAccountChooserUsability(AccountChooserUsabilityMetric usability) {
   UMA_HISTOGRAM_ENUMERATION("PasswordManager.AccountChooserDialogUsability",
                             usability, ACCOUNT_CHOOSER_USABILITY_COUNT);
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 9582c88..c2377f1 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -216,6 +216,9 @@
 // Log a user action on showing the Chrome sign in promo.
 void LogSyncSigninPromoUserAction(SyncSignInUserAction action);
 
+// Logs whether a password was rejected due to same origin but different scheme.
+void LogShouldBlockPasswordForSameOriginButDifferentScheme(bool should_block);
+
 // Log if the account chooser has empty username or duplicate usernames.
 void LogAccountChooserUsability(AccountChooserUsabilityMetric usability);
 
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index f45f191..5ce7d40 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -33,6 +33,7 @@
 using testing::_;
 using testing::AnyNumber;
 using testing::Return;
+using testing::ReturnRef;
 using testing::SaveArg;
 using testing::WithArg;
 
@@ -67,6 +68,7 @@
                void(const autofill::PasswordForm&));
   MOCK_METHOD0(AutomaticPasswordSaveIndicator, void());
   MOCK_METHOD0(GetPrefs, PrefService*());
+  MOCK_CONST_METHOD0(GetMainFrameURL, const GURL&());
   MOCK_METHOD0(GetDriver, PasswordManagerDriver*());
   MOCK_CONST_METHOD0(GetStoreResultFilter, const MockStoreResultFilter*());
 
@@ -135,6 +137,9 @@
         .WillRepeatedly(Return(password_autofill_manager_.get()));
     EXPECT_CALL(client_, DidLastPageLoadEncounterSSLErrors())
         .WillRepeatedly(Return(false));
+
+    ON_CALL(client_, GetMainFrameURL())
+        .WillByDefault(ReturnRef(GURL::EmptyGURL()));
   }
 
   void TearDown() override {
@@ -748,6 +753,104 @@
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 }
 
+TEST_F(PasswordManagerTest,
+       ShouldBlockPasswordForSameOriginButDifferentSchemeTest) {
+  constexpr struct {
+    const char* old_origin;
+    const char* new_origin;
+    bool result;
+  } kTestData[] = {
+      // Same origin and same scheme.
+      {"https://example.com/login", "https://example.com/login", false},
+      // Same host and same scheme, different port.
+      {"https://example.com:443/login", "https://example.com:444/login", false},
+      // Same host but different scheme (https to http).
+      {"https://example.com/login", "http://example.com/login", true},
+      // Same host but different scheme (http to https).
+      {"http://example.com/login", "https://example.com/login", false},
+      // Different TLD, same schemes.
+      {"https://example.com/login", "https://example.org/login", false},
+      // Different TLD, different schemes.
+      {"https://example.com/login", "http://example.org/login", false},
+      // Different subdomains, same schemes.
+      {"https://sub1.example.com/login", "https://sub2.example.org/login",
+       false},
+  };
+
+  PasswordForm form = MakeSimpleForm();
+  for (const auto& test_case : kTestData) {
+    SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
+    manager()->main_frame_url_ = GURL(test_case.old_origin);
+    form.origin = GURL(test_case.new_origin);
+    EXPECT_EQ(
+        test_case.result,
+        manager()->ShouldBlockPasswordForSameOriginButDifferentScheme(form));
+  }
+}
+
+// Tests whether two submissions to the same origin but different schemes
+// result in only saving the first submission, which has a secure scheme.
+TEST_F(PasswordManagerTest, AttemptedSavePasswordSameOriginInsecureScheme) {
+  PasswordForm secure_form(MakeSimpleForm());
+  secure_form.origin = GURL("https://example.com/login");
+  secure_form.action = GURL("https://example.com/login");
+  secure_form.signon_realm = secure_form.origin.spec();
+
+  PasswordForm insecure_form(MakeSimpleForm());
+  insecure_form.username_value = ASCIIToUTF16("compromised_user");
+  insecure_form.password_value = ASCIIToUTF16("C0mpr0m1s3d_P4ss");
+  insecure_form.origin = GURL("http://example.com/home");
+  insecure_form.action = GURL("http://example.com/home");
+  insecure_form.signon_realm = insecure_form.origin.spec();
+
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+
+  EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(client_, GetMainFrameURL())
+      .WillRepeatedly(ReturnRef(secure_form.origin));
+
+  // Parse, render and submit the secure form.
+  std::vector<PasswordForm> observed = {secure_form};
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+  OnPasswordFormSubmitted(secure_form);
+
+  // Make sure |PromptUserToSaveOrUpdatePassword| gets called, and the resulting
+  // form manager is saved.
+  std::unique_ptr<PasswordFormManager> form_manager_to_save;
+  EXPECT_CALL(client_,
+              PromptUserToSaveOrUpdatePasswordPtr(
+                  _, CredentialSourceType::CREDENTIAL_SOURCE_PASSWORD_MANAGER))
+      .WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
+
+  EXPECT_CALL(client_, GetMainFrameURL())
+      .WillRepeatedly(ReturnRef(insecure_form.origin));
+
+  // Parse, render and submit the insecure form.
+  observed = {insecure_form};
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+  OnPasswordFormSubmitted(insecure_form);
+
+  // Expect no further calls to |ProptUserToSaveOrUpdatePassword| due to
+  // insecure origin.
+  EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_, _)).Times(0);
+
+  // Trigger call to |ProvisionalSavePassword| by rendering a page without
+  // forms.
+  observed.clear();
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+  // Make sure that the form saved by the user is indeed the secure form.
+  ASSERT_TRUE(form_manager_to_save);
+  EXPECT_THAT(form_manager_to_save->pending_credentials(),
+              FormMatches(secure_form));
+}
+
 // Create a form with both a new and current password element. Let the current
 // password value be non-empty and the new password value be empty and submit
 // the form. While normally saving the new password is preferred (on change
diff --git a/components/startup_metric_utils/OWNERS b/components/startup_metric_utils/OWNERS
index 221025c9..711005f 100644
--- a/components/startup_metric_utils/OWNERS
+++ b/components/startup_metric_utils/OWNERS
@@ -1,2 +1,4 @@
 fdoray@chromium.org
+
+# To increase bus factor but prefer above OWNERS :).
 gab@chromium.org
diff --git a/components/task_scheduler_util/browser/initialization.cc b/components/task_scheduler_util/browser/initialization.cc
index 3f1624b9..70b28773 100644
--- a/components/task_scheduler_util/browser/initialization.cc
+++ b/components/task_scheduler_util/browser/initialization.cc
@@ -60,7 +60,7 @@
 size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
   const bool is_background =
       traits.priority() == base::TaskPriority::BACKGROUND;
-  if (traits.with_file_io())
+  if (traits.may_block() || traits.with_base_sync_primitives())
     return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
   return is_background ? BACKGROUND : FOREGROUND;
 }
diff --git a/content/DEPS b/content/DEPS
index ce566ff..3d29bf1 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -36,7 +36,6 @@
   "+mojo/common",
   "+mojo/edk/embedder",
   "+mojo/edk/js",
-  "+mojo/edk/test",
   "+mojo/message_pump",
   "+mojo/public",
   "+net",
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 78d21f9..d6d917b 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -463,7 +463,7 @@
 size_t DefaultBrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
   const bool is_background =
       traits.priority() == base::TaskPriority::BACKGROUND;
-  if (traits.with_file_io())
+  if (traits.may_block() || traits.with_base_sync_primitives())
     return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
 
   return is_background ? BACKGROUND : FOREGROUND;
@@ -1698,7 +1698,8 @@
   }
 
   mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)));
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST));
 
   service_manager_context_.reset(new ServiceManagerContext);
 #if defined(OS_MACOSX)
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index ec4c476..9a41e0c 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -42,6 +42,8 @@
   IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ForwardInputEvent, OnForwardInputEvent)
     IPC_MESSAGE_HANDLER(FrameHostMsg_FrameRectChanged, OnFrameRectChanged)
+    IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection,
+                        OnUpdateViewportIntersection)
     IPC_MESSAGE_HANDLER(FrameHostMsg_VisibilityChanged, OnVisibilityChanged)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SatisfySequence, OnSatisfySequence)
     IPC_MESSAGE_HANDLER(FrameHostMsg_RequireSequence, OnRequireSequence)
@@ -288,6 +290,12 @@
     SetRect(frame_rect);
 }
 
+void CrossProcessFrameConnector::OnUpdateViewportIntersection(
+    const gfx::Rect& viewport_intersection) {
+  if (view_)
+    view_->UpdateViewportIntersection(viewport_intersection);
+}
+
 void CrossProcessFrameConnector::OnVisibilityChanged(bool visible) {
   if (!view_)
     return;
diff --git a/content/browser/frame_host/cross_process_frame_connector.h b/content/browser/frame_host/cross_process_frame_connector.h
index 2b2160d1..6af5108 100644
--- a/content/browser/frame_host/cross_process_frame_connector.h
+++ b/content/browser/frame_host/cross_process_frame_connector.h
@@ -144,6 +144,7 @@
   // Handlers for messages received from the parent frame.
   void OnForwardInputEvent(const blink::WebInputEvent* event);
   void OnFrameRectChanged(const gfx::Rect& frame_rect);
+  void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection);
   void OnVisibilityChanged(bool visible);
   void OnSatisfySequence(const cc::SurfaceSequence& sequence);
   void OnRequireSequence(const cc::SurfaceId& id,
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 ac8340ef..0c6bc7df 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
@@ -336,6 +336,13 @@
   }
 }
 
+void RenderWidgetHostViewChildFrame::UpdateViewportIntersection(
+    const gfx::Rect& viewport_intersection) {
+  if (host_)
+    host_->Send(new ViewMsg_SetViewportIntersection(host_->GetRoutingID(),
+                                                    viewport_intersection));
+}
+
 void RenderWidgetHostViewChildFrame::GestureEventAck(
     const blink::WebGestureEvent& event,
     InputEventAckState ack_result) {
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.h b/content/browser/frame_host/render_widget_host_view_child_frame.h
index 9af289d..4c228e4 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.h
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.h
@@ -194,6 +194,8 @@
   void RegisterFrameSinkId();
   void UnregisterFrameSinkId();
 
+  void UpdateViewportIntersection(const gfx::Rect& viewport_intersection);
+
  protected:
   friend class RenderWidgetHostView;
   friend class RenderWidgetHostViewChildFrameTest;
diff --git a/content/browser/loader/DEPS b/content/browser/loader/DEPS
index 58169f05..8a128b3 100644
--- a/content/browser/loader/DEPS
+++ b/content/browser/loader/DEPS
@@ -345,6 +345,7 @@
   "upload_progress_tracker\.(cc|h)": [
     "-content",
     "+content/browser/loader/upload_progress_tracker.h",
+    "+content/common/content_export.h",
   ],
   "url_loader_factory_impl\.(cc|h)": [
     "-content",
diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc
index d55bc0f9..7503da6 100644
--- a/content/browser/loader/async_resource_handler.cc
+++ b/content/browser/loader/async_resource_handler.cc
@@ -34,6 +34,7 @@
 #include "ipc/ipc_message_macros.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
+#include "net/base/upload_progress.h"
 #include "net/url_request/redirect_info.h"
 
 using base::TimeDelta;
@@ -553,13 +554,13 @@
   inlining_helper_->RecordHistogram(elapsed_time);
 }
 
-void AsyncResourceHandler::SendUploadProgress(int64_t current_position,
-                                              int64_t total_size) {
+void AsyncResourceHandler::SendUploadProgress(
+    const net::UploadProgress& progress) {
   ResourceMessageFilter* filter = GetFilter();
   if (!filter)
     return;
   filter->Send(new ResourceMsg_UploadProgress(
-      GetRequestID(), current_position, total_size));
+      GetRequestID(), progress.position(), progress.size()));
 }
 
 }  // namespace content
diff --git a/content/browser/loader/async_resource_handler.h b/content/browser/loader/async_resource_handler.h
index fb69ec7..dbf3b035 100644
--- a/content/browser/loader/async_resource_handler.h
+++ b/content/browser/loader/async_resource_handler.h
@@ -20,6 +20,7 @@
 
 namespace net {
 class URLRequest;
+class UploadProgress;
 }
 
 namespace content {
@@ -67,7 +68,7 @@
   int CalculateEncodedDataLengthToReport();
   int CalculateEncodedBodyLengthToReport();
   void RecordHistogram();
-  void SendUploadProgress(int64_t current_position, int64_t total_size);
+  void SendUploadProgress(const net::UploadProgress& progress);
 
   scoped_refptr<ResourceBuffer> buffer_;
   ResourceDispatcherHostImpl* rdh_;
diff --git a/content/browser/loader/upload_progress_tracker.cc b/content/browser/loader/upload_progress_tracker.cc
index 790bea4..6633e61 100644
--- a/content/browser/loader/upload_progress_tracker.cc
+++ b/content/browser/loader/upload_progress_tracker.cc
@@ -18,11 +18,12 @@
 UploadProgressTracker::UploadProgressTracker(
     const tracked_objects::Location& location,
     UploadProgressReportCallback report_progress,
-    net::URLRequest* request)
+    net::URLRequest* request,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : request_(request), report_progress_(std::move(report_progress)) {
-  DCHECK(request_);
   DCHECK(report_progress_);
 
+  progress_timer_.SetTaskRunner(std::move(task_runner));
   progress_timer_.Start(location, kUploadProgressInterval, this,
                         &UploadProgressTracker::ReportUploadProgressIfNeeded);
 }
@@ -39,22 +40,33 @@
   progress_timer_.Stop();
 }
 
+base::TimeTicks UploadProgressTracker::GetCurrentTime() const {
+  return base::TimeTicks::Now();
+}
+
+net::UploadProgress UploadProgressTracker::GetUploadProgress() const {
+  return request_->GetUploadProgress();
+}
+
 void UploadProgressTracker::ReportUploadProgressIfNeeded() {
   if (waiting_for_upload_progress_ack_)
     return;
 
-  net::UploadProgress progress = request_->GetUploadProgress();
+  net::UploadProgress progress = GetUploadProgress();
   if (!progress.size())
-    return;  // Nothing to upload.
+    return;  // Nothing to upload, or in the chunked upload mode.
 
-  if (progress.position() == last_upload_position_)
-    return;  // No progress made since last time.
+  // No progress made since last time, or the progress was reset by a redirect
+  // or a retry.
+  if (progress.position() <= last_upload_position_)
+    return;
 
   const uint64_t kHalfPercentIncrements = 200;
   const base::TimeDelta kOneSecond = base::TimeDelta::FromMilliseconds(1000);
 
   uint64_t amt_since_last = progress.position() - last_upload_position_;
-  base::TimeDelta time_since_last = base::TimeTicks::Now() - last_upload_ticks_;
+  base::TimeTicks now = GetCurrentTime();
+  base::TimeDelta time_since_last = now - last_upload_ticks_;
 
   bool is_finished = (progress.size() == progress.position());
   bool enough_new_progress =
@@ -62,9 +74,9 @@
   bool too_much_time_passed = time_since_last > kOneSecond;
 
   if (is_finished || enough_new_progress || too_much_time_passed) {
-    report_progress_.Run(progress.position(), progress.size());
+    report_progress_.Run(progress);
     waiting_for_upload_progress_ack_ = true;
-    last_upload_ticks_ = base::TimeTicks::Now();
+    last_upload_ticks_ = now;
     last_upload_position_ = progress.position();
   }
 }
diff --git a/content/browser/loader/upload_progress_tracker.h b/content/browser/loader/upload_progress_tracker.h
index 4172490b..6840f5a6 100644
--- a/content/browser/loader/upload_progress_tracker.h
+++ b/content/browser/loader/upload_progress_tracker.h
@@ -9,8 +9,17 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "content/common/content_export.h"
+#include "net/base/upload_progress.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
 
 namespace tracked_objects {
 class Location;
@@ -24,20 +33,26 @@
 
 // UploadProgressTracker watches the upload progress of a URL loading, and sends
 // the progress to the client in a suitable granularity and frequency.
-class UploadProgressTracker final {
+class CONTENT_EXPORT UploadProgressTracker {
  public:
   using UploadProgressReportCallback =
-      base::RepeatingCallback<void(int64_t, int64_t)>;
+      base::RepeatingCallback<void(const net::UploadProgress&)>;
 
   UploadProgressTracker(const tracked_objects::Location& location,
                         UploadProgressReportCallback report_progress,
-                        net::URLRequest* request);
+                        net::URLRequest* request,
+                        scoped_refptr<base::SingleThreadTaskRunner>
+                            task_runner = base::ThreadTaskRunnerHandle::Get());
   ~UploadProgressTracker();
 
   void OnAckReceived();
   void OnUploadCompleted();
 
  private:
+  // Overridden by tests to use a fake time and progress.
+  virtual base::TimeTicks GetCurrentTime() const;
+  virtual net::UploadProgress GetUploadProgress() const;
+
   void ReportUploadProgressIfNeeded();
 
   net::URLRequest* request_;  // Not owned.
diff --git a/content/browser/loader/upload_progress_tracker_unittest.cc b/content/browser/loader/upload_progress_tracker_unittest.cc
new file mode 100644
index 0000000..508368d
--- /dev/null
+++ b/content/browser/loader/upload_progress_tracker_unittest.cc
@@ -0,0 +1,256 @@
+// 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 "content/browser/loader/upload_progress_tracker.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "net/base/upload_progress.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+class TestingUploadProgressTracker : public UploadProgressTracker {
+ public:
+  TestingUploadProgressTracker(
+      const tracked_objects::Location& location,
+      UploadProgressReportCallback report_callback,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : UploadProgressTracker(location,
+                              std::move(report_callback),
+                              nullptr,
+                              std::move(task_runner)),
+        current_time_(base::TimeTicks::Now()) {}
+
+  void set_upload_progress(const net::UploadProgress& upload_progress) {
+    upload_progress_ = upload_progress;
+  }
+
+  void set_current_time(const base::TimeTicks& current_time) {
+    current_time_ = current_time;
+  }
+
+ private:
+  // UploadProgressTracker overrides.
+  base::TimeTicks GetCurrentTime() const override { return current_time_; }
+  net::UploadProgress GetUploadProgress() const override {
+    return upload_progress_;
+  }
+
+  base::TimeTicks current_time_;
+  net::UploadProgress upload_progress_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingUploadProgressTracker);
+};
+
+}  // namespace
+
+class UploadProgressTrackerTest : public ::testing::Test {
+ public:
+  UploadProgressTrackerTest()
+      : task_runner_(new base::TestSimpleTaskRunner),
+        upload_progress_tracker_(
+            FROM_HERE,
+            base::BindRepeating(
+                &UploadProgressTrackerTest::OnUploadProgressReported,
+                base::Unretained(this)),
+            task_runner_) {}
+
+ private:
+  void OnUploadProgressReported(const net::UploadProgress& progress) {
+    ++report_count_;
+    reported_position_ = progress.position();
+    reported_total_size_ = progress.size();
+  }
+
+ protected:
+  int report_count_ = 0;
+  int64_t reported_position_ = 0;
+  int64_t reported_total_size_ = 0;
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  TestingUploadProgressTracker upload_progress_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadProgressTrackerTest);
+};
+
+TEST_F(UploadProgressTrackerTest, NoACK) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(750, 1000));
+
+  // The second timer task does nothing, since the first report didn't send the
+  // ACK.
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+}
+
+TEST_F(UploadProgressTrackerTest, NoUpload) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(0, 0));
+
+  // UploadProgressTracker does nothing on the empty upload content.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(0, report_count_);
+}
+
+TEST_F(UploadProgressTrackerTest, NoProgress) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.OnAckReceived();
+
+  // The second time doesn't call ReportUploadProgress since there's no
+  // progress.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+}
+
+TEST_F(UploadProgressTrackerTest, Finished) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(999, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(999, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.OnAckReceived();
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(1000, 1000));
+
+  // The second timer task calls ReportUploadProgress for reporting the
+  // completion.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(2, report_count_);
+  EXPECT_EQ(1000, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+}
+
+TEST_F(UploadProgressTrackerTest, Progress) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.OnAckReceived();
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(750, 1000));
+
+  // The second timer task calls ReportUploadProgress since the progress is
+  // big enough to report.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(2, report_count_);
+  EXPECT_EQ(750, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+}
+
+TEST_F(UploadProgressTrackerTest, TimePassed) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.OnAckReceived();
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(501, 1000));
+
+  // The second timer task doesn't call ReportUploadProgress since the progress
+  // is too small to report it.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+
+  upload_progress_tracker_.set_current_time(base::TimeTicks::Now() +
+                                            base::TimeDelta::FromSeconds(1));
+
+  // The third timer task calls ReportUploadProgress since it's been long time
+  // from the last report.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(2, report_count_);
+  EXPECT_EQ(501, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+}
+
+TEST_F(UploadProgressTrackerTest, Rewound) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.OnAckReceived();
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(250, 1000));
+
+  // The second timer task doesn't call ReportUploadProgress since the progress
+  // was rewound.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+
+  upload_progress_tracker_.set_current_time(base::TimeTicks::Now() +
+                                            base::TimeDelta::FromSeconds(1));
+
+  // Even after a good amount of time passed, the rewound progress should not be
+  // reported.
+  EXPECT_EQ(1, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+}
+
+TEST_F(UploadProgressTrackerTest, Completed) {
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(500, 1000));
+
+  // The first timer task calls ReportUploadProgress.
+  EXPECT_EQ(0, report_count_);
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(1, report_count_);
+  EXPECT_EQ(500, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  upload_progress_tracker_.set_upload_progress(net::UploadProgress(1000, 1000));
+
+  // OnUploadCompleted runs ReportUploadProgress even without Ack nor timer.
+  upload_progress_tracker_.OnUploadCompleted();
+  EXPECT_EQ(2, report_count_);
+  EXPECT_EQ(1000, reported_position_);
+  EXPECT_EQ(1000, reported_total_size_);
+
+  task_runner_->RunPendingTasks();
+  EXPECT_EQ(2, report_count_);
+  EXPECT_FALSE(task_runner_->HasPendingTask());
+}
+
+}  // namespace context
diff --git a/content/browser/resources/media/peer_connection_update_table.js b/content/browser/resources/media/peer_connection_update_table.js
index f3475a8..d6795d5 100644
--- a/content/browser/resources/media/peer_connection_update_table.js
+++ b/content/browser/resources/media/peer_connection_update_table.js
@@ -99,6 +99,15 @@
         return;
       }
 
+      if (update.type === 'onIceCandidate' ||
+          update.type === 'addIceCandidate') {
+        // extract ICE candidate type from the field following typ.
+        var candidateType = update.value.match(
+          /(?: typ )(host|srflx|relay)/)[1];
+        if (candidateType) {
+          type += ' (' + candidateType + ')';
+        }
+      }
       row.innerHTML += '<td><details><summary>' + type +
           '</summary></details></td>';
 
@@ -106,6 +115,14 @@
       var details = row.cells[1].childNodes[0];
       details.appendChild(valueContainer);
 
+      // Highlight ICE failures and failure callbacks.
+      if ((update.type === 'iceConnectionStateChange' &&
+           update.value === 'ICEConnectionStateFailed') ||
+          update.type.indexOf('OnFailure') !== -1 ||
+          update.type === 'addIceCandidateFailed') {
+        valueContainer.parentElement.classList.add('update-log-failure');
+      }
+
       var value = update.value;
       // map internal names and values to names and events from the
       // specification. This is a display change which shall not
diff --git a/content/browser/resources/media/webrtc_internals.css b/content/browser/resources/media/webrtc_internals.css
index cf6f31a..b8111e19 100644
--- a/content/browser/resources/media/webrtc_internals.css
+++ b/content/browser/resources/media/webrtc_internals.css
@@ -14,6 +14,10 @@
   overflow: auto;
 }
 
+.update-log-failure {
+  background-color: #be2026;
+}
+
 .ssrc-info-block {
   color: #999;
   font-size: 0.8em;
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 7cf6e74..aa9b94f 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -477,6 +477,7 @@
     std::unique_ptr<EmbeddedWorkerStartParams> params,
     mojom::ServiceWorkerEventDispatcherRequest dispatcher_request,
     const StatusCallback& callback) {
+  restart_count_++;
   if (!context_) {
     callback.Run(SERVICE_WORKER_ERROR_ABORT);
     // |this| may be destroyed by the callback.
@@ -597,6 +598,7 @@
       embedded_worker_id_(embedded_worker_id),
       status_(EmbeddedWorkerStatus::STOPPED),
       starting_phase_(NOT_STARTING),
+      restart_count_(0),
       thread_id_(kInvalidEmbeddedWorkerThreadId),
       devtools_attached_(false),
       network_accessed_for_script_(false),
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 3152613..2054db8 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -136,6 +136,7 @@
     DCHECK_EQ(EmbeddedWorkerStatus::STARTING, status());
     return starting_phase_;
   }
+  int restart_count() const { return restart_count_; }
   int process_id() const;
   int thread_id() const { return thread_id_; }
   // This should be called only when the worker instance has a valid process,
@@ -296,6 +297,7 @@
 
   EmbeddedWorkerStatus status_;
   StartingPhase starting_phase_;
+  int restart_count_;
 
   // Current running information.
   std::unique_ptr<EmbeddedWorkerInstance::WorkerProcessHandle> process_handle_;
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index c0e32ce..e0574c1 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_side_navigation_policy.h"
+#include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/origin_util.h"
 #include "ipc/ipc_message_macros.h"
@@ -1087,6 +1088,15 @@
     base::debug::ScopedCrashKey scope_provider_host_pid(
         "swdh_set_hosted_version_host_pid",
         base::IntToString(provider_host->process_id()));
+    if (version->embedded_worker()->process_id() !=
+        ChildProcessHost::kInvalidUniqueID) {
+      base::debug::ScopedCrashKey scope_is_new_process(
+          "swdh_set_hosted_version_is_new_process",
+          version->embedded_worker()->is_new_process() ? "true" : "false");
+    }
+    base::debug::ScopedCrashKey scope_worker_restart_count(
+        "swdh_set_hosted_version_restart_count",
+        base::IntToString(version->embedded_worker()->restart_count()));
     bad_message::ReceivedBadMessage(
         this, bad_message::SWDH_SET_HOSTED_VERSION_PROCESS_MISMATCH);
     return;
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index d2c4660..2666ba2 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -433,7 +433,8 @@
 
   if (!IsInBrowserProcess()) {
     // Don't double-initialize IPC support in single-process mode.
-    mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport(GetIOTaskRunner()));
+    mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport(
+        GetIOTaskRunner(), mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST));
     InitializeMojoIPCChannel();
   }
   std::string service_request_token;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 5eb31e75d..c2d8c47 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -1315,6 +1315,11 @@
 // position of a child's ancestor has changed).
 IPC_MESSAGE_ROUTED1(FrameHostMsg_FrameRectChanged, gfx::Rect /* frame_rect */)
 
+// Sent by a parent frame to update its child's viewport intersection rect for
+// use by the IntersectionObserver API.
+IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdateViewportIntersection,
+                    gfx::Rect /* viewport_intersection */)
+
 // Informs the child that the frame has changed visibility.
 IPC_MESSAGE_ROUTED1(FrameHostMsg_VisibilityChanged, bool /* visible */)
 
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 3c9d75e..7bf0b14b 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -608,6 +608,10 @@
 IPC_MESSAGE_ROUTED1(ViewMsg_HandleCompositorProto,
                     std::vector<uint8_t> /* proto */)
 
+// Sets the viewport intersection on the widget for an out-of-process iframe.
+IPC_MESSAGE_ROUTED1(ViewMsg_SetViewportIntersection,
+                    gfx::Rect /* viewport_intersection */)
+
 // -----------------------------------------------------------------------------
 // Messages sent from the renderer to the browser.
 
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index fed88d5..83679237 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -237,8 +237,9 @@
   // Initialize mojo firstly to enable Blink initialization to use it.
   InitializeMojo();
   test_io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
-  ipc_support_.reset(
-      new mojo::edk::test::ScopedIPCSupport(test_io_thread_->task_runner()));
+  ipc_support_.reset(new mojo::edk::ScopedIPCSupport(
+      test_io_thread_->task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST));
 
   // Blink needs to be initialized before calling CreateContentRendererClient()
   // because it uses blink internally.
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h
index 2e8a521..a14e4ec 100644
--- a/content/public/test/render_view_test.h
+++ b/content/public/test/render_view_test.h
@@ -19,7 +19,7 @@
 #include "content/public/common/main_function_params.h"
 #include "content/public/common/page_state.h"
 #include "content/public/test/mock_render_thread.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/Platform.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
@@ -210,7 +210,7 @@
 
   // For Mojo.
   std::unique_ptr<base::TestIOThread> test_io_thread_;
-  std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipc_support_;
+  std::unique_ptr<mojo::edk::ScopedIPCSupport> ipc_support_;
 
 #if defined(OS_MACOSX)
   std::unique_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool_;
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc
index 211ecdfa..e9251c3 100644
--- a/content/public/test/test_browser_thread_bundle.cc
+++ b/content/public/test/test_browser_thread_bundle.cc
@@ -17,11 +17,13 @@
     : TestBrowserThreadBundle(DEFAULT) {}
 
 TestBrowserThreadBundle::TestBrowserThreadBundle(int options)
-    : options_(options), threads_started_(false) {
+    : options_(options), threads_created_(false) {
   Init();
 }
 
 TestBrowserThreadBundle::~TestBrowserThreadBundle() {
+  DCHECK(threads_created_);
+
   // To avoid memory leaks, we must ensure that any tasks posted to the blocking
   // pool via PostTaskAndReply are able to reply back to the originating thread.
   // Thus we must flush the blocking pool while the browser threads still exist.
@@ -65,23 +67,34 @@
 void TestBrowserThreadBundle::Init() {
   // Check for conflicting options can't have two IO threads.
   CHECK(!(options_ & IO_MAINLOOP) || !(options_ & REAL_IO_THREAD));
-  // There must be a thread to start to use DONT_START_THREADS
-  CHECK((options_ & ~IO_MAINLOOP) != DONT_START_THREADS);
+  // There must be a thread to start to use DONT_CREATE_THREADS
+  CHECK((options_ & ~IO_MAINLOOP) != DONT_CREATE_THREADS);
 
+  // Create the UI thread. In production, this work is done in
+  // BrowserMainLoop::MainMessageLoopStart().
   if (options_ & IO_MAINLOOP) {
     message_loop_.reset(new base::MessageLoopForIO());
   } else {
     message_loop_.reset(new base::MessageLoopForUI());
   }
 
-  task_scheduler_.reset(
-      new base::test::ScopedTaskScheduler(message_loop_.get()));
-
   ui_thread_.reset(
       new TestBrowserThread(BrowserThread::UI, message_loop_.get()));
 
+  if (!(options_ & DONT_CREATE_THREADS))
+    CreateThreads();
+}
+
+// This method mimics the work done in BrowserMainLoop::CreateThreads().
+void TestBrowserThreadBundle::CreateThreads() {
+  DCHECK(!threads_created_);
+
+  task_scheduler_.reset(
+      new base::test::ScopedTaskScheduler(message_loop_.get()));
+
   if (options_ & REAL_DB_THREAD) {
     db_thread_.reset(new TestBrowserThread(BrowserThread::DB));
+    db_thread_->Start();
   } else {
     db_thread_.reset(
         new TestBrowserThread(BrowserThread::DB, message_loop_.get()));
@@ -89,6 +102,7 @@
 
   if (options_ & REAL_FILE_THREAD) {
     file_thread_.reset(new TestBrowserThread(BrowserThread::FILE));
+    file_thread_->Start();
   } else {
     file_thread_.reset(
         new TestBrowserThread(BrowserThread::FILE, message_loop_.get()));
@@ -103,28 +117,13 @@
 
   if (options_ & REAL_IO_THREAD) {
     io_thread_.reset(new TestBrowserThread(BrowserThread::IO));
+    io_thread_->StartIOThread();
   } else {
     io_thread_.reset(
         new TestBrowserThread(BrowserThread::IO, message_loop_.get()));
   }
 
-  if (!(options_ & DONT_START_THREADS))
-    Start();
-}
-
-void TestBrowserThreadBundle::Start() {
-  DCHECK(!threads_started_);
-
-  if (options_ & REAL_DB_THREAD)
-    db_thread_->Start();
-
-  if (options_ & REAL_FILE_THREAD)
-    file_thread_->Start();
-
-  if (options_ & REAL_IO_THREAD)
-    io_thread_->StartIOThread();
-
-  threads_started_ = true;
+  threads_created_ = true;
 }
 
 }  // namespace content
diff --git a/content/public/test/test_browser_thread_bundle.h b/content/public/test/test_browser_thread_bundle.h
index 0138a723..4a6b030 100644
--- a/content/public/test/test_browser_thread_bundle.h
+++ b/content/public/test/test_browser_thread_bundle.h
@@ -26,13 +26,11 @@
 // REAL_IO_THREAD.
 //
 // For some tests it is important to emulate real browser startup. During real
-// browser startup some initialization is done (e.g. creation of thread objects)
-// between creating the main thread message loop, which is bound to the existing
-// main thread, and starting the other threads. Passing DONT_START_THREADS to
-// constructor will delay staring these other threads until the test explicitly
-// calls Start().
+// browser startup, the main MessageLoop is created before other threads.
+// Passing DONT_CREATE_THREADS to constructor will delay creating other threads
+// until the test explicitly calls CreateThreads().
 //
-// DONT_START_THREADS should only be used when the options specify at least
+// DONT_CREATE_THREADS should only be used when the options specify at least
 // one real thread other than the main thread.
 
 #ifndef CONTENT_PUBLIC_TEST_TEST_BROWSER_THREAD_BUNDLE_H_
@@ -64,15 +62,15 @@
     REAL_DB_THREAD = 0x02,
     REAL_FILE_THREAD = 0x08,
     REAL_IO_THREAD = 0x10,
-    DONT_START_THREADS = 0x20,
+    DONT_CREATE_THREADS = 0x20,
   };
 
   TestBrowserThreadBundle();
   explicit TestBrowserThreadBundle(int options);
 
-  // Start the real threads; should only be called from other classes if the
-  // DONT_START_THREADS option was used when the bundle was created.
-  void Start();
+  // Creates threads; should only be called from other classes if the
+  // DONT_CREATE_THREADS option was used when the bundle was created.
+  void CreateThreads();
 
   ~TestBrowserThreadBundle();
 
@@ -90,7 +88,7 @@
   std::unique_ptr<TestBrowserThread> io_thread_;
 
   int options_;
-  bool threads_started_;
+  bool threads_created_;
 
   DISALLOW_COPY_AND_ASSIGN(TestBrowserThreadBundle);
 };
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index b268ad01..abfdbfe 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -701,8 +701,8 @@
       "//third_party/opus",
       "//third_party/webrtc/api:libjingle_peerconnection",
       "//third_party/webrtc/api:rtc_stats_api",
+      "//third_party/webrtc/api:video_frame_api",
       "//third_party/webrtc/base:rtc_base",
-      "//third_party/webrtc/common_video",
       "//third_party/webrtc/media:rtc_media",
       "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/audio_device",
diff --git a/content/renderer/media/gpu/rtc_video_decoder.cc b/content/renderer/media/gpu/rtc_video_decoder.cc
index 929ab57..63239737 100644
--- a/content/renderer/media/gpu/rtc_video_decoder.cc
+++ b/content/renderer/media/gpu/rtc_video_decoder.cc
@@ -18,10 +18,10 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/renderers/gpu_video_accelerator_factories.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/webrtc/api/video/video_frame.h"
 #include "third_party/webrtc/base/bind.h"
 #include "third_party/webrtc/base/refcount.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
-#include "third_party/webrtc/video_frame.h"
 
 #if defined(OS_WIN)
 #include "base/command_line.h"
diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc
index c95f429..759e621 100644
--- a/content/renderer/media/media_stream_video_source.cc
+++ b/content/renderer/media/media_stream_video_source.cc
@@ -106,7 +106,10 @@
     }
   }
   for (const auto& constraint_set : constraints.advanced()) {
-    if (constraint_set.aspectRatio.hasMax()) {
+    // Advanced constraint sets with max aspect ratio 0 are unsatisfiable and
+    // must be ignored.
+    if (constraint_set.aspectRatio.hasMax() &&
+        constraint_set.aspectRatio.max() > 0) {
       *max_aspect_ratio = constraint_set.aspectRatio.max();
       break;
     }
diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc
index bfb572d..dfeea8e 100644
--- a/content/renderer/media/media_stream_video_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_source_unittest.cc
@@ -782,4 +782,24 @@
   sink.DisconnectFromTrack();
 }
 
+// Test that an optional constraint with an invalid aspect ratio is ignored.
+TEST_F(MediaStreamVideoSourceTest, InvalidOptionalAspectRatioIgnored) {
+  MockConstraintFactory factory;
+  factory.AddAdvanced().aspectRatio.setMax(0.0);
+  blink::WebMediaStreamTrack track =
+      CreateTrack("123", factory.CreateWebMediaConstraints());
+  mock_source()->CompleteGetSupportedFormats();
+  EXPECT_EQ(0, NumberOfFailedConstraintsCallbacks());
+}
+
+// Test that setting an invalid mandatory aspect ratio fails.
+TEST_F(MediaStreamVideoSourceTest, InvalidMandatoryAspectRatioFails) {
+  MockConstraintFactory factory;
+  factory.basic().aspectRatio.setMax(0.0);
+  blink::WebMediaStreamTrack track =
+      CreateTrack("123", factory.CreateWebMediaConstraints());
+  mock_source()->CompleteGetSupportedFormats();
+  EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
+}
+
 }  // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
index d5bdd1c..84daf7e 100644
--- a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
@@ -21,7 +21,7 @@
 #include "third_party/libyuv/include/libyuv/convert_from.h"
 #include "third_party/libyuv/include/libyuv/scale.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/webrtc/common_video/rotation.h"
+#include "third_party/webrtc/api/video/video_rotation.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_video_frame_adapter.h b/content/renderer/media/webrtc/webrtc_video_frame_adapter.h
index fadd508..47b4518 100644
--- a/content/renderer/media/webrtc/webrtc_video_frame_adapter.h
+++ b/content/renderer/media/webrtc/webrtc_video_frame_adapter.h
@@ -9,7 +9,7 @@
 
 #include "base/callback.h"
 #include "media/base/video_frame.h"
-#include "third_party/webrtc/common_video/include/video_frame_buffer.h"
+#include "third_party/webrtc/api/video/video_frame_buffer.h"
 
 namespace content {
 // Thin adapter from media::VideoFrame to webrtc::VideoFrameBuffer. This
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index c318049c..fe58b65b 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -30,6 +30,7 @@
 #include "ipc/ipc_message_macros.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebFeaturePolicy.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
@@ -492,6 +493,12 @@
   Send(new FrameHostMsg_FrameRectChanged(routing_id_, rect));
 }
 
+void RenderFrameProxy::updateRemoteViewportIntersection(
+    const blink::WebRect& viewportIntersection) {
+  Send(new FrameHostMsg_UpdateViewportIntersection(
+      routing_id_, gfx::Rect(viewportIntersection)));
+}
+
 void RenderFrameProxy::visibilityChanged(bool visible) {
   Send(new FrameHostMsg_VisibilityChanged(routing_id_, visible));
 }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index e7f2e54..3de7d667 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -131,6 +131,8 @@
                 bool should_replace_current_entry) override;
   void forwardInputEvent(const blink::WebInputEvent* event) override;
   void frameRectsChanged(const blink::WebRect& frame_rect) override;
+  void updateRemoteViewportIntersection(
+      const blink::WebRect& viewportIntersection) override;
   void visibilityChanged(bool visible) override;
   void setHasReceivedUserGesture() override;
   void didChangeOpener(blink::WebFrame* opener) override;
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index e837e4f..180e34fe 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -120,7 +120,7 @@
 size_t DefaultRendererWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
   const bool is_background =
       traits.priority() == base::TaskPriority::BACKGROUND;
-  if (traits.with_file_io())
+  if (traits.may_block() || traits.with_base_sync_primitives())
     return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
 
   return is_background ? BACKGROUND : FOREGROUND;
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index dcdc702..af28029 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -44,7 +44,6 @@
 #include "ipc/ipc.mojom.h"
 #include "ipc/ipc_channel_mojo.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h"
 #include "ui/gfx/buffer_format_util.h"
@@ -179,7 +178,6 @@
         base::ThreadTaskRunnerHandle::Get();
 
     InitializeMojo();
-    ipc_support_.reset(new mojo::edk::test::ScopedIPCSupport(io_task_runner));
     shell_context_.reset(new TestServiceManagerContext);
     child_connection_.reset(new ChildConnection(
         mojom::kRendererServiceName, "test", mojo::edk::GenerateRandomToken(),
@@ -241,7 +239,6 @@
   std::unique_ptr<ContentRendererClient> content_renderer_client_;
 
   std::unique_ptr<TestBrowserThreadBundle> browser_threads_;
-  std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipc_support_;
   std::unique_ptr<TestServiceManagerContext> shell_context_;
   std::unique_ptr<ChildConnection> child_connection_;
   std::unique_ptr<IPC::ChannelProxy> channel_;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index d72800f..a38ef25 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -619,6 +619,8 @@
     IPC_MESSAGE_HANDLER(ViewMsg_SetTextDirection, OnSetTextDirection)
     IPC_MESSAGE_HANDLER(ViewMsg_Move_ACK, OnRequestMoveAck)
     IPC_MESSAGE_HANDLER(ViewMsg_UpdateScreenRects, OnUpdateScreenRects)
+    IPC_MESSAGE_HANDLER(ViewMsg_SetViewportIntersection,
+                        OnSetViewportIntersection)
     IPC_MESSAGE_HANDLER(ViewMsg_WaitForNextFrameForTests,
                         OnWaitNextFrameForTests)
     IPC_MESSAGE_HANDLER(InputMsg_RequestCompositionUpdate,
@@ -1652,6 +1654,15 @@
     window_screen_rect_ = window_screen_rect;
 }
 
+void RenderWidget::OnSetViewportIntersection(
+    const gfx::Rect& viewport_intersection) {
+  if (GetWebWidget() && GetWebWidget()->isWebFrameWidget()) {
+    DCHECK(popup_type_ == WebPopupType::WebPopupTypeNone);
+    static_cast<WebFrameWidget*>(GetWebWidget())
+        ->setRemoteViewportIntersection(viewport_intersection);
+  }
+}
+
 void RenderWidget::OnHandleCompositorProto(const std::vector<uint8_t>& proto) {
   if (compositor_)
     compositor_->OnHandleCompositorProto(proto);
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 745c913..e6390ede 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -510,6 +510,7 @@
   void OnUpdateScreenRects(const gfx::Rect& view_screen_rect,
                            const gfx::Rect& window_screen_rect);
   void OnUpdateWindowScreenRect(const gfx::Rect& window_screen_rect);
+  void OnSetViewportIntersection(const gfx::Rect& viewport_intersection);
   void OnHandleCompositorProto(const std::vector<uint8_t>& proto);
   // Real data that is dragged is not included at DragEnter time.
   void OnDragTargetDragEnter(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4a6a2b6..8e5bce0 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -249,7 +249,6 @@
     "//media/capture",
     "//media/capture/mojo:image_capture",
     "//mojo/edk/system",
-    "//mojo/edk/test:test_support",
     "//net:test_support",
     "//services/service_manager/public/cpp",
     "//skia",
@@ -718,7 +717,6 @@
     "//media/audio:test_support",
     "//media/base:test_support",
     "//mojo/edk/system",
-    "//mojo/edk/test:test_support",
     "//mojo/public/cpp/bindings",
     "//mojo/public/js",
     "//net:test_support",
@@ -1129,6 +1127,7 @@
     "../browser/loader/test_url_loader_client.h",
     "../browser/loader/throttling_resource_handler_unittest.cc",
     "../browser/loader/upload_data_stream_builder_unittest.cc",
+    "../browser/loader/upload_progress_tracker_unittest.cc",
     "../browser/loader/url_loader_factory_impl_unittest.cc",
     "../browser/mach_broker_mac_unittest.cc",
     "../browser/media/audible_metrics_unittest.cc",
@@ -1451,7 +1450,7 @@
     "//media/capture/mojo:capture_types",
     "//media/midi:midi",
     "//media/midi:mojo",
-    "//mojo/edk/test:test_support",
+    "//mojo/edk/system",
     "//mojo/public/cpp/bindings",
     "//net:extras",
     "//net:test_support",
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 48e8177..9dba4daf 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1111,6 +1111,7 @@
         'fyi_only': True,
         # Run this on the optional tryservers.
         'run_on_optional': True,
+        'os_types': ['win', 'linux'],
       },
     ],
   },
diff --git a/content/test/run_all_unittests.cc b/content/test/run_all_unittests.cc
index 7efb88b..02d678c2 100644
--- a/content/test/run_all_unittests.cc
+++ b/content/test/run_all_unittests.cc
@@ -9,16 +9,15 @@
 #include "build/build_config.h"
 #include "content/public/test/unittest_test_suite.h"
 #include "content/test/content_test_suite.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 
 int main(int argc, char** argv) {
   content::UnitTestTestSuite test_suite(
       new content::ContentTestSuite(argc, argv));
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipc_support;
-  ipc_support.reset(
-      new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner()));
-
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
   return base::LaunchUnitTests(
       argc, argv, base::Bind(&content::UnitTestTestSuite::Run,
                              base::Unretained(&test_suite)));
diff --git a/device/vr/vr_service_impl.cc b/device/vr/vr_service_impl.cc
index f8003706..9d0fb36 100644
--- a/device/vr/vr_service_impl.cc
+++ b/device/vr/vr_service_impl.cc
@@ -17,6 +17,10 @@
 VRServiceImpl::VRServiceImpl() : listening_for_activate_(false) {}
 
 VRServiceImpl::~VRServiceImpl() {
+  // Destroy VRDisplay before calling RemoveService below. RemoveService might
+  // implicitly trigger destory VRDevice which VRDisplay needs to access in its
+  // dtor.
+  displays_.clear();
   VRDeviceManager::GetInstance()->RemoveService(this);
 }
 
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index af2ed165..bf50a92 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -210,7 +210,6 @@
     "//extensions/renderer:unit_tests",
     "//extensions/shell:unit_tests",
     "//extensions/utility:unit_tests",
-    "//mojo/edk/test:test_support",
     "//ui/gl:test_support",
   ]
 
diff --git a/extensions/test/DEPS b/extensions/test/DEPS
index 2e42e816..186feed 100644
--- a/extensions/test/DEPS
+++ b/extensions/test/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
   "+content/public",
-  "+mojo/edk/test",
   "+ui/gl",
 ]
diff --git a/extensions/test/extensions_unittests_main.cc b/extensions/test/extensions_unittests_main.cc
index 96480f6..8b72ef8 100644
--- a/extensions/test/extensions_unittests_main.cc
+++ b/extensions/test/extensions_unittests_main.cc
@@ -16,7 +16,6 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_paths.h"
 #include "extensions/test/test_extensions_client.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 #include "url/url_util.h"
@@ -100,10 +99,6 @@
 
 int main(int argc, char** argv) {
   content::UnitTestTestSuite test_suite(new ExtensionsTestSuite(argc, argv));
-
-  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
-
   return base::LaunchUnitTests(argc,
                                argv,
                                base::Bind(&content::UnitTestTestSuite::Run,
diff --git a/google_apis/gcm/engine/connection_event_tracker.cc b/google_apis/gcm/engine/connection_event_tracker.cc
index 0ebd798..50a5ecdcc 100644
--- a/google_apis/gcm/engine/connection_event_tracker.cc
+++ b/google_apis/gcm/engine/connection_event_tracker.cc
@@ -66,8 +66,8 @@
 void ConnectionEventTracker::ConnectionLoginFailed() {
   // A login failure would have originally been marked as a successful
   // connection, so now that it failed, that needs to be updated.
-  // TODO(harkness): Add back DCHECK which was removed. See
-  // https://crbug.com/673706.
+  DCHECK_EQ(current_event_.type(),
+            mcs_proto::ClientEvent::SUCCESSFUL_CONNECTION);
 
   current_event_.set_type(mcs_proto::ClientEvent::FAILED_CONNECTION);
   current_event_.clear_time_connection_established_ms();
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index 153e5bc..2d459f2 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -66,7 +66,7 @@
       connecting_(false),
       waiting_for_backoff_(false),
       waiting_for_network_online_(false),
-      logging_in_(false),
+      handshake_in_progress_(false),
       recorder_(recorder),
       listener_(NULL),
       weak_ptr_factory_(this) {
@@ -131,7 +131,7 @@
 void ConnectionFactoryImpl::ConnectWithBackoff() {
   // If a canary managed to connect while a backoff expiration was pending,
   // just cleanup the internal state.
-  if (connecting_ || logging_in_ || IsEndpointReachable()) {
+  if (connecting_ || handshake_in_progress_ || IsEndpointReachable()) {
     waiting_for_backoff_ = false;
     return;
   }
@@ -167,8 +167,8 @@
 std::string ConnectionFactoryImpl::GetConnectionStateString() const {
   if (IsEndpointReachable())
     return "CONNECTED";
-  if (logging_in_)
-    return "LOGGING IN";
+  if (handshake_in_progress_)
+    return "HANDSHAKE IN PROGRESS";
   if (connecting_)
     return "CONNECTING";
   if (waiting_for_backoff_)
@@ -209,7 +209,7 @@
     // connection.
   }
 
-  if (logging_in_)
+  if (reason == LOGIN_FAILURE)
     event_tracker_.ConnectionLoginFailed();
   event_tracker_.EndConnectionAttempt();
 
@@ -233,9 +233,9 @@
     // effect if we're already in the process of connecting.
     ConnectImpl();
     return;
-  } else if (logging_in_) {
-    // Failures prior to login completion just reuse the existing backoff entry.
-    logging_in_ = false;
+  } else if (handshake_in_progress_) {
+    // Failures prior to handshake completion reuse the existing backoff entry.
+    handshake_in_progress_ = false;
     backoff_entry_->InformOfRequest(false);
   } else if (reason == LOGIN_FAILURE ||
              ShouldRestorePreviousBackoff(last_login_time_, NowTicks())) {
@@ -412,7 +412,7 @@
   last_successful_endpoint_ = next_endpoint_;
   next_endpoint_ = 0;
   connecting_ = false;
-  logging_in_ = true;
+  handshake_in_progress_ = true;
   DVLOG(1) << "MCS endpoint socket connection success, starting login.";
   InitHandler();
 }
@@ -434,7 +434,7 @@
   last_login_time_ = NowTicks();
   previous_backoff_.swap(backoff_entry_);
   backoff_entry_->Reset();
-  logging_in_ = false;
+  handshake_in_progress_ = false;
 
   event_tracker_.ConnectionAttemptSucceeded();
 
diff --git a/google_apis/gcm/engine/connection_factory_impl.h b/google_apis/gcm/engine/connection_factory_impl.h
index 1af8aac0..14836ef 100644
--- a/google_apis/gcm/engine/connection_factory_impl.h
+++ b/google_apis/gcm/engine/connection_factory_impl.h
@@ -184,10 +184,10 @@
   // client is informed of a valid connection type.
   bool waiting_for_network_online_;
 
-  // Whether login successfully completed after the connection was established.
-  // If a connection reset happens while attempting to log in, the current
-  // backoff entry is reused (after incrementing with a new failure).
-  bool logging_in_;
+  // Whether handshake is in progress after the connection was established. If
+  // a connection reset happens while attempting to complete the handshake, the
+  // current backoff entry is reused (after incrementing with a new failure).
+  bool handshake_in_progress_;
 
   // The time of the last login completion. Used for calculating whether to
   // restore a previous backoff entry and for measuring uptime.
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 6c28e708..047f408 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -75,6 +75,8 @@
 
     HeadlessBrowserContext::Builder context_builder =
         browser_->CreateBrowserContextBuilder();
+    // TODO(eseckler): These switches should also affect BrowserContexts that
+    // are created via DevTools later.
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kDeterministicFetch)) {
       deterministic_dispatcher_.reset(
@@ -98,6 +100,7 @@
           }));
     }
     browser_context_ = context_builder.Build();
+    browser_->SetDefaultBrowserContext(browser_context_);
 
     HeadlessWebContents::Builder builder(
         browser_context_->CreateWebContentsBuilder());
diff --git a/headless/lib/browser/headless_browser_impl.cc b/headless/lib/browser/headless_browser_impl.cc
index 876be95..93d6918 100644
--- a/headless/lib/browser/headless_browser_impl.cc
+++ b/headless/lib/browser/headless_browser_impl.cc
@@ -54,6 +54,7 @@
     : on_start_callback_(on_start_callback),
       options_(std::move(options)),
       browser_main_parts_(nullptr),
+      default_browser_context_(nullptr),
       weak_ptr_factory_(this) {}
 
 HeadlessBrowserImpl::~HeadlessBrowserImpl() {}
@@ -158,6 +159,19 @@
   auto it = browser_contexts_.find(browser_context->Id());
   DCHECK(it != browser_contexts_.end());
   browser_contexts_.erase(it);
+  if (default_browser_context_ == browser_context)
+    SetDefaultBrowserContext(nullptr);
+}
+
+void HeadlessBrowserImpl::SetDefaultBrowserContext(
+    HeadlessBrowserContext* browser_context) {
+  DCHECK(!browser_context ||
+         this == HeadlessBrowserContextImpl::From(browser_context)->browser());
+  default_browser_context_ = browser_context;
+}
+
+HeadlessBrowserContext* HeadlessBrowserImpl::GetDefaultBrowserContext() {
+  return default_browser_context_;
 }
 
 base::WeakPtr<HeadlessBrowserImpl> HeadlessBrowserImpl::GetWeakPtr() {
diff --git a/headless/lib/browser/headless_browser_impl.h b/headless/lib/browser/headless_browser_impl.h
index 74a4b6c..f37dff1d 100644
--- a/headless/lib/browser/headless_browser_impl.h
+++ b/headless/lib/browser/headless_browser_impl.h
@@ -51,6 +51,9 @@
       const std::string& devtools_agent_host_id) override;
   HeadlessBrowserContext* GetBrowserContextForId(
       const std::string& id) override;
+  void SetDefaultBrowserContext(
+      HeadlessBrowserContext* browser_context) override;
+  HeadlessBrowserContext* GetDefaultBrowserContext() override;
 
   void set_browser_main_parts(HeadlessBrowserMainParts* browser_main_parts);
   HeadlessBrowserMainParts* browser_main_parts() const;
@@ -82,6 +85,7 @@
 
   std::unordered_map<std::string, std::unique_ptr<HeadlessBrowserContextImpl>>
       browser_contexts_;
+  HeadlessBrowserContext* default_browser_context_;  // Not owned.
 
   base::WeakPtrFactory<HeadlessBrowserImpl> weak_ptr_factory_;
 
diff --git a/headless/lib/browser/headless_devtools_manager_delegate.cc b/headless/lib/browser/headless_devtools_manager_delegate.cc
index 0c82f334a..4144038 100644
--- a/headless/lib/browser/headless_devtools_manager_delegate.cc
+++ b/headless/lib/browser/headless_devtools_manager_delegate.cc
@@ -20,9 +20,57 @@
 
 namespace headless {
 
+namespace {
+const char kIdParam[] = "id";
+const char kResultParam[] = "result";
+const char kErrorParam[] = "error";
+const char kErrorCodeParam[] = "code";
+const char kErrorMessageParam[] = "message";
+
+// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object
+enum Error {
+  kErrorInvalidParam = -32602,
+  kErrorServerError = -32000
+};
+
+std::unique_ptr<base::DictionaryValue> CreateSuccessResponse(
+    int command_id,
+    std::unique_ptr<base::Value> result) {
+  if (!result)
+    result.reset(new base::DictionaryValue());
+
+  std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+  response->SetInteger(kIdParam, command_id);
+  response->Set(kResultParam, std::move(result));
+  return response;
+}
+
+std::unique_ptr<base::DictionaryValue> CreateErrorResponse(
+    int command_id,
+    int error_code,
+    const std::string& error_message) {
+  std::unique_ptr<base::DictionaryValue> error_object(
+      new base::DictionaryValue());
+  error_object->SetInteger(kErrorCodeParam, error_code);
+  error_object->SetString(kErrorMessageParam, error_message);
+
+  std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+  response->Set(kErrorParam, std::move(error_object));
+  return response;
+}
+
+std::unique_ptr<base::DictionaryValue> CreateInvalidParamResponse(
+    int command_id,
+    const std::string& param) {
+  return CreateErrorResponse(
+      command_id, kErrorInvalidParam,
+      base::StringPrintf("Missing or invalid '%s' parameter", param.c_str()));
+}
+}  // namespace
+
 HeadlessDevToolsManagerDelegate::HeadlessDevToolsManagerDelegate(
     base::WeakPtr<HeadlessBrowserImpl> browser)
-    : browser_(std::move(browser)), default_browser_context_(nullptr) {
+    : browser_(std::move(browser)) {
   command_map_["Target.createTarget"] =
       &HeadlessDevToolsManagerDelegate::CreateTarget;
   command_map_["Target.closeTarget"] =
@@ -55,19 +103,15 @@
   if (find_it == command_map_.end())
     return nullptr;
   CommandMemberFnPtr command_fn_ptr = find_it->second;
-  std::unique_ptr<base::Value> cmd_result(((this)->*command_fn_ptr)(params));
-  if (!cmd_result)
-    return nullptr;
-
-  std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
-  result->SetInteger("id", id);
-  result->Set("result", std::move(cmd_result));
-  return result.release();
+  std::unique_ptr<base::DictionaryValue> cmd_result(
+      ((this)->*command_fn_ptr)(id, params));
+  return cmd_result.release();
 }
 
 std::string HeadlessDevToolsManagerDelegate::GetDiscoveryPageHTML() {
-  return ResourceBundle::GetSharedInstance().GetRawDataResource(
-      IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE).as_string();
+  return ResourceBundle::GetSharedInstance()
+      .GetRawDataResource(IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE)
+      .as_string();
 }
 
 std::string HeadlessDevToolsManagerDelegate::GetFrontendResource(
@@ -75,7 +119,9 @@
   return content::DevToolsFrontendHost::GetFrontendResource(path).as_string();
 }
 
-std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CreateTarget(
+std::unique_ptr<base::DictionaryValue>
+HeadlessDevToolsManagerDelegate::CreateTarget(
+    int command_id,
     const base::DictionaryValue* params) {
   std::string url;
   std::string browser_context_id;
@@ -86,15 +132,20 @@
   params->GetInteger("width", &width);
   params->GetInteger("height", &height);
 
-  // TODO(alexclarke): Should we fail when user passes incorrect id?
   HeadlessBrowserContext* context =
       browser_->GetBrowserContextForId(browser_context_id);
-  if (!context) {
-    if (!default_browser_context_) {
-      default_browser_context_ =
-          browser_->CreateBrowserContextBuilder().Build();
+  if (!browser_context_id.empty()) {
+    context = browser_->GetBrowserContextForId(browser_context_id);
+    if (!context)
+      return CreateInvalidParamResponse(command_id, "browserContextId");
+  } else {
+    context = browser_->GetDefaultBrowserContext();
+    if (!context) {
+      return CreateErrorResponse(command_id, kErrorServerError,
+                                 "You specified no |browserContextId|, but "
+                                 "there is no default browser context set on "
+                                 "HeadlessBrowser");
     }
-    context = default_browser_context_;
   }
 
   HeadlessWebContentsImpl* web_contents_impl =
@@ -103,18 +154,21 @@
                                         .SetWindowSize(gfx::Size(width, height))
                                         .Build());
 
-  return target::CreateTargetResult::Builder()
-      .SetTargetId(web_contents_impl->GetDevToolsAgentHostId())
-      .Build()
-      ->Serialize();
+  std::unique_ptr<base::Value> result(
+      target::CreateTargetResult::Builder()
+          .SetTargetId(web_contents_impl->GetDevToolsAgentHostId())
+          .Build()
+          ->Serialize());
+  return CreateSuccessResponse(command_id, std::move(result));
 }
 
-std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CloseTarget(
+std::unique_ptr<base::DictionaryValue>
+HeadlessDevToolsManagerDelegate::CloseTarget(
+    int command_id,
     const base::DictionaryValue* params) {
   std::string target_id;
-  if (!params->GetString("targetId", &target_id)) {
-    return nullptr;
-  }
+  if (!params->GetString("targetId", &target_id))
+    return CreateInvalidParamResponse(command_id, "targetId");
   HeadlessWebContents* web_contents =
       browser_->GetWebContentsForDevToolsAgentHostId(target_id);
   bool success = false;
@@ -122,46 +176,51 @@
     web_contents->Close();
     success = true;
   }
-  return target::CloseTargetResult::Builder()
-      .SetSuccess(success)
-      .Build()
-      ->Serialize();
+  std::unique_ptr<base::Value> result(target::CloseTargetResult::Builder()
+                                          .SetSuccess(success)
+                                          .Build()
+                                          ->Serialize());
+  return CreateSuccessResponse(command_id, std::move(result));
 }
 
-std::unique_ptr<base::Value>
+std::unique_ptr<base::DictionaryValue>
 HeadlessDevToolsManagerDelegate::CreateBrowserContext(
+    int command_id,
     const base::DictionaryValue* params) {
   HeadlessBrowserContext* browser_context =
       browser_->CreateBrowserContextBuilder().Build();
 
-  std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
-  return target::CreateBrowserContextResult::Builder()
-      .SetBrowserContextId(browser_context->Id())
-      .Build()
-      ->Serialize();
+  std::unique_ptr<base::Value> result(
+      target::CreateBrowserContextResult::Builder()
+          .SetBrowserContextId(browser_context->Id())
+          .Build()
+          ->Serialize());
+  return CreateSuccessResponse(command_id, std::move(result));
 }
 
-std::unique_ptr<base::Value>
+std::unique_ptr<base::DictionaryValue>
 HeadlessDevToolsManagerDelegate::DisposeBrowserContext(
+    int command_id,
     const base::DictionaryValue* params) {
   std::string browser_context_id;
-  if (!params->GetString("browserContextId", &browser_context_id)) {
-    return nullptr;
-  }
+  if (!params->GetString("browserContextId", &browser_context_id))
+    return CreateInvalidParamResponse(command_id, "browserContextId");
   HeadlessBrowserContext* context =
       browser_->GetBrowserContextForId(browser_context_id);
 
   bool success = false;
-  if (context && context != default_browser_context_ &&
+  if (context && context != browser_->GetDefaultBrowserContext() &&
       context->GetAllWebContents().empty()) {
     success = true;
     context->Close();
   }
 
-  return target::DisposeBrowserContextResult::Builder()
-      .SetSuccess(success)
-      .Build()
-      ->Serialize();
+  std::unique_ptr<base::Value> result(
+      target::DisposeBrowserContextResult::Builder()
+          .SetSuccess(success)
+          .Build()
+          ->Serialize());
+  return CreateSuccessResponse(command_id, std::move(result));
 }
 
 }  // namespace headless
diff --git a/headless/lib/browser/headless_devtools_manager_delegate.h b/headless/lib/browser/headless_devtools_manager_delegate.h
index 3b800d53..c891e82 100644
--- a/headless/lib/browser/headless_devtools_manager_delegate.h
+++ b/headless/lib/browser/headless_devtools_manager_delegate.h
@@ -16,7 +16,6 @@
 
 namespace headless {
 class HeadlessBrowserImpl;
-class HeadlessBrowserContext;
 
 class HeadlessDevToolsManagerDelegate
     : public content::DevToolsManagerDelegate {
@@ -32,22 +31,26 @@
   std::string GetFrontendResource(const std::string& path) override;
 
  private:
-  std::unique_ptr<base::Value> CreateTarget(
+  std::unique_ptr<base::DictionaryValue> CreateTarget(
+      int command_id,
       const base::DictionaryValue* params);
-  std::unique_ptr<base::Value> CloseTarget(const base::DictionaryValue* params);
-  std::unique_ptr<base::Value> CreateBrowserContext(
+  std::unique_ptr<base::DictionaryValue> CloseTarget(
+      int command_id,
       const base::DictionaryValue* params);
-  std::unique_ptr<base::Value> DisposeBrowserContext(
+  std::unique_ptr<base::DictionaryValue> CreateBrowserContext(
+      int command_id,
+      const base::DictionaryValue* params);
+  std::unique_ptr<base::DictionaryValue> DisposeBrowserContext(
+      int command_id,
       const base::DictionaryValue* params);
 
   base::WeakPtr<HeadlessBrowserImpl> browser_;
 
-  using CommandMemberFnPtr = std::unique_ptr<base::Value> (
-      HeadlessDevToolsManagerDelegate::*)(const base::DictionaryValue* params);
+  using CommandMemberFnPtr = std::unique_ptr<base::DictionaryValue> (
+      HeadlessDevToolsManagerDelegate::*)(int command_id,
+                                          const base::DictionaryValue* params);
 
   std::map<std::string, CommandMemberFnPtr> command_map_;
-
-  HeadlessBrowserContext* default_browser_context_;
 };
 
 }  // namespace headless
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h
index 021e910..c3043f00 100644
--- a/headless/public/headless_browser.h
+++ b/headless/public/headless_browser.h
@@ -56,6 +56,12 @@
   virtual HeadlessBrowserContext* GetBrowserContextForId(
       const std::string& id) = 0;
 
+  // Allows setting and getting the browser context that DevTools will create
+  // new targets in by default.
+  virtual void SetDefaultBrowserContext(
+      HeadlessBrowserContext* browser_context) = 0;
+  virtual HeadlessBrowserContext* GetDefaultBrowserContext() = 0;
+
   // Returns a task runner for submitting work to the browser file thread.
   virtual scoped_refptr<base::SingleThreadTaskRunner> BrowserFileThread()
       const = 0;
diff --git a/headless/test/headless_browser_test.cc b/headless/test/headless_browser_test.cc
index 3e1f0f2..53e2db2 100644
--- a/headless/test/headless_browser_test.cc
+++ b/headless/test/headless_browser_test.cc
@@ -184,7 +184,8 @@
 }
 
 HeadlessAsyncDevTooledBrowserTest::HeadlessAsyncDevTooledBrowserTest()
-    : web_contents_(nullptr),
+    : browser_context_(nullptr),
+      web_contents_(nullptr),
       devtools_client_(HeadlessDevToolsClient::Create()),
       render_process_exited_(false) {}
 
@@ -209,6 +210,7 @@
 
 void HeadlessAsyncDevTooledBrowserTest::RunTest() {
   browser_context_ = browser()->CreateBrowserContextBuilder().Build();
+  browser()->SetDefaultBrowserContext(browser_context_);
 
   web_contents_ = browser_context_->CreateWebContentsBuilder().Build();
   web_contents_->AddObserver(this);
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 5b0bda09..21abdeef5 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -135,6 +135,7 @@
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
+#import "ios/third_party/material_roboto_font_loader_ios/src/src/MDCTypographyAdditions/MDFRobotoFontLoader+MDCTypographyAdditions.h"
 #include "ios/web/net/request_tracker_factory_impl.h"
 #include "ios/web/net/request_tracker_impl.h"
 #include "ios/web/net/web_http_protocol_handler_delegate.h"
@@ -230,12 +231,6 @@
 
 }  // namespace
 
-// TODO(crbug.com/673904): Remove once MDFRobotoFontLoader declares it directly.
-// MDFRobotoFontLoader implicitly implements MDCTypographyFontLoading but can't
-// declare it until MDC is public.
-@interface MDFRobotoFontLoader (MDCTypography)<MDCTypographyFontLoading>
-@end
-
 @interface MainController ()<BrowserStateStorageSwitching,
                              BrowsingDataRemovalControllerDelegate,
                              PrefObserverDelegate,
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 0ca0279..0ca1db6 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1203,6 +1203,9 @@
       <message name="IDS_IOS_SETTING_ON" desc="Generic status label displayed in Settings to show that a setting is currently turned on. [Length: 5em] [iOS only]">
         On
       </message>
+      <message name="IDS_IOS_TOGGLE_SETTING_SWITCH_ACCESSIBILITY_HINT" desc="Action hint for any switch in settings. This is spoken by VoiceOver. [iOS only]">
+        Double tap to toggle setting
+      </message>
       <message name="IDS_IOS_SHARE_EMAIL_COMPLETE" desc="Message shown when email share has been sent. [Length: Unknown. Keep it short.] [iOS only]">
         Mail sent.
       </message>
diff --git a/ios/chrome/browser/payments/OWNERS b/ios/chrome/browser/payments/OWNERS
index ca512d20e..4cfb56d 100644
--- a/ios/chrome/browser/payments/OWNERS
+++ b/ios/chrome/browser/payments/OWNERS
@@ -1 +1,2 @@
-jdonnelly@chromium.org
+lpromero@chromium.org
+mahmadi@chromium.org
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 113c3f26..3509d68 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -3914,7 +3914,6 @@
 
   switch (command) {
     case IDC_BACK:
-      // TODO(crbug.com/677160): Remove |canGoBack| check.
       if ([_model currentTab].canGoBack) {
         [[_model currentTab] goBack];
       }
@@ -3958,7 +3957,6 @@
       [self searchFindInPage];
       break;
     case IDC_FORWARD:
-      // TODO(crbug.com/677160): Remove |canGoForward| check.
       if ([_model currentTab].canGoForward) {
         [[_model currentTab] goForward];
       }
diff --git a/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm b/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
index cd6f56b8..b427959b 100644
--- a/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
+++ b/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
@@ -5,8 +5,10 @@
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
 
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -31,7 +33,6 @@
   if (self) {
     self.cellClass = [CollectionViewSwitchCell class];
     self.enabled = YES;
-    self.accessibilityTraits |= UIAccessibilityTraitButton;
   }
   return self;
 }
@@ -74,6 +75,8 @@
     _switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
     _switchView.translatesAutoresizingMaskIntoConstraints = NO;
     _switchView.onTintColor = [[MDCPalette cr_bluePalette] tint500];
+    _switchView.accessibilityHint = l10n_util::GetNSString(
+        IDS_IOS_TOGGLE_SETTING_SWITCH_ACCESSIBILITY_HINT);
     [self.contentView addSubview:_switchView];
 
     // Set up the constraints.
@@ -138,7 +141,11 @@
 }
 
 - (NSString*)accessibilityHint {
-  return _switchView.accessibilityHint;
+  if (_switchView.isEnabled) {
+    return _switchView.accessibilityHint;
+  } else {
+    return @"";
+  }
 }
 
 - (NSString*)accessibilityLabel {
@@ -146,7 +153,11 @@
 }
 
 - (NSString*)accessibilityValue {
-  return _switchView.accessibilityValue;
+  if (_switchView.on) {
+    return l10n_util::GetNSString(IDS_IOS_SETTING_ON);
+  } else {
+    return l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
index 4a2da9834..c8c9779 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.h
@@ -30,7 +30,7 @@
 
 // Update the match type icon with the supplied image ID and adjust its position
 // based on the current size of the row.
-- (void)updateLeftImage:(int)imageID;
+- (void)updateLeadingImage:(int)imageID;
 
 @end
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
index 71f2f21..9d066dc 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_row.mm
@@ -17,6 +17,7 @@
 const CGFloat kLeadingPaddingIpad = 164;
 const CGFloat kLeadingPaddingIpadCompact = 71;
 const CGFloat kAppendButtonTrailingMargin = 4;
+const CGFloat kAppendButtonSize = 48.0;
 }
 
 @interface OmniboxPopupMaterialRow () {
@@ -78,7 +79,7 @@
     [self updatePhysicalWebImage];
     [self addSubview:_physicalWebButton];
 
-    // Left icon is only displayed on iPad.
+    // Leading icon is only displayed on iPad.
     if (IsIPadIdiom()) {
       _imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
       _imageView.userInteractionEnabled = NO;
@@ -110,19 +111,16 @@
       kImageDimensionLength);
   _imageView.frame = LayoutRectGetRect(imageViewLayout);
 
-  CGFloat appendButtonDimensionLength = CGRectGetHeight(self.bounds);
-  DCHECK_GT(appendButtonDimensionLength, 40);
-  LayoutRect rightAccessoryLayout =
-      LayoutRectMake(CGRectGetWidth(self.bounds) - appendButtonDimensionLength -
-                         kAppendButtonTrailingMargin,
-                     CGRectGetWidth(self.bounds),
-                     floor((_rowHeight - appendButtonDimensionLength) / 2),
-                     appendButtonDimensionLength, appendButtonDimensionLength);
-  _appendButton.frame = LayoutRectGetRect(rightAccessoryLayout);
-  _physicalWebButton.frame = LayoutRectGetRect(rightAccessoryLayout);
+  LayoutRect trailingAccessoryLayout = LayoutRectMake(
+      CGRectGetWidth(self.bounds) - kAppendButtonSize -
+          kAppendButtonTrailingMargin,
+      CGRectGetWidth(self.bounds), floor((_rowHeight - kAppendButtonSize) / 2),
+      kAppendButtonSize, kAppendButtonSize);
+  _appendButton.frame = LayoutRectGetRect(trailingAccessoryLayout);
+  _physicalWebButton.frame = LayoutRectGetRect(trailingAccessoryLayout);
 }
 
-- (void)updateLeftImage:(int)imageID {
+- (void)updateLeadingImage:(int)imageID {
   _imageView.image = NativeImage(imageID);
 
   // Adjust the vertical position based on the current size of the row.
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
index f408e9c2..732a94f 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.mm
@@ -370,11 +370,12 @@
   }
   [textLabel setNeedsDisplay];
 
-  // The left image (e.g. magnifying glass, star, clock) is only shown on iPad.
+  // The leading image (e.g. magnifying glass, star, clock) is only shown on
+  // iPad.
   if (IsIPadIdiom()) {
     int imageId = GetIconForAutocompleteMatchType(
         match.type, _popupView->IsStarredMatch(match), _incognito);
-    [row updateLeftImage:imageId];
+    [row updateLeadingImage:imageId];
   }
 
   // Show append button for search history/search suggestions/voice search as
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm b/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
index 35ebc135..0d97812 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
@@ -186,8 +186,7 @@
   if (authenticatedIdentity) {
     title = [authenticatedIdentity userFullName];
     if (!title) {
-      // TODO(crbug.com/656994): Figure how to handle unnamed account.
-      title = @"Unnamed account";
+      title = [authenticatedIdentity userEmail];
     }
   }
   self.title = title;
diff --git a/ios/chrome/browser/ui/settings/cells/sync_switch_item.mm b/ios/chrome/browser/ui/settings/cells/sync_switch_item.mm
index e3d070c..d8191ee5 100644
--- a/ios/chrome/browser/ui/settings/cells/sync_switch_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/sync_switch_item.mm
@@ -5,8 +5,10 @@
 #import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
 
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -38,7 +40,6 @@
   if (self) {
     self.cellClass = [SyncSwitchCell class];
     self.enabled = YES;
-    self.accessibilityTraits |= UIAccessibilityTraitButton;
   }
   return self;
 }
@@ -96,6 +97,8 @@
     _switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
     _switchView.translatesAutoresizingMaskIntoConstraints = NO;
     _switchView.onTintColor = [[MDCPalette cr_bluePalette] tint500];
+    _switchView.accessibilityHint = l10n_util::GetNSString(
+        IDS_IOS_TOGGLE_SETTING_SWITCH_ACCESSIBILITY_HINT);
     [self.contentView addSubview:_switchView];
 
     [self setConstraints];
@@ -179,7 +182,11 @@
 }
 
 - (NSString*)accessibilityHint {
-  return _switchView.accessibilityHint;
+  if (_switchView.isEnabled) {
+    return _switchView.accessibilityHint;
+  } else {
+    return @"";
+  }
 }
 
 - (NSString*)accessibilityLabel {
@@ -191,7 +198,11 @@
 }
 
 - (NSString*)accessibilityValue {
-  return _switchView.accessibilityValue;
+  if (_switchView.on) {
+    return l10n_util::GetNSString(IDS_IOS_SETTING_ON);
+  } else {
+    return l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
+  }
 }
 
 @end
diff --git a/ios/chrome/share_extension/share_extension_view.mm b/ios/chrome/share_extension/share_extension_view.mm
index 1e0f73c8..a072574 100644
--- a/ios/chrome/share_extension/share_extension_view.mm
+++ b/ios/chrome/share_extension/share_extension_view.mm
@@ -60,9 +60,10 @@
 // Keep strong references of the views that need to be updated.
 @property(nonatomic, strong) UILabel* titleLabel;
 @property(nonatomic, strong) UILabel* URLLabel;
+@property(nonatomic, strong) UIView* titleURLContainer;
 @property(nonatomic, strong) UIButton* readingListButton;
 @property(nonatomic, strong) UIImageView* screenshotView;
-@property(nonatomic, strong) UIStackView* itemStack;
+@property(nonatomic, strong) UIView* itemView;
 
 // View creation helpers.
 // Returns a view containing the shared items (title, URL, screenshot). This
@@ -99,9 +100,10 @@
 
 @synthesize titleLabel = _titleLabel;
 @synthesize URLLabel = _URLLabel;
+@synthesize titleURLContainer = _titleURLContainer;
 @synthesize readingListButton = _readingListButton;
 @synthesize screenshotView = _screenshotView;
-@synthesize itemStack = _itemStack;
+@synthesize itemView = _itemView;
 
 #pragma mark - Lifecycle
 
@@ -179,15 +181,17 @@
 
   // Screenshot view. Image will be filled by |setScreenshot:| when available.
   _screenshotView = [[UIImageView alloc] initWithFrame:CGRectZero];
-  [_screenshotView.widthAnchor
-      constraintLessThanOrEqualToConstant:kScreenshotSize]
-      .active = YES;
+  [_screenshotView setTranslatesAutoresizingMaskIntoConstraints:NO];
+  NSLayoutConstraint* imageWidthConstraint =
+      [_screenshotView.widthAnchor constraintEqualToConstant:0];
+  imageWidthConstraint.priority = UILayoutPriorityDefaultHigh;
+  imageWidthConstraint.active = YES;
+
   [_screenshotView.heightAnchor
       constraintEqualToAnchor:_screenshotView.widthAnchor]
       .active = YES;
   [_screenshotView setContentMode:UIViewContentModeScaleAspectFill];
   [_screenshotView setClipsToBounds:YES];
-  [_screenshotView setHidden:YES];
 
   // |_screenshotView| should take as much space as needed. Lower compression
   // resistance of the other elements.
@@ -203,52 +207,58 @@
       setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                       forAxis:UILayoutConstraintAxisHorizontal];
 
-  UIStackView* titleURLStack = [[UIStackView alloc]
-      initWithArrangedSubviews:@[ _titleLabel, _URLLabel ]];
-  [titleURLStack setAxis:UILayoutConstraintAxisVertical];
+  _titleURLContainer = [[UIView alloc] initWithFrame:CGRectZero];
+  [_titleURLContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
 
-  UIView* titleURLContainer = [[UIView alloc] initWithFrame:CGRectZero];
-  [titleURLContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [titleURLContainer addSubview:titleURLStack];
-  [[titleURLStack topAnchor]
-      constraintEqualToAnchor:[titleURLContainer topAnchor]
-                     constant:kShareExtensionPadding]
-      .active = YES;
-  [[titleURLStack bottomAnchor]
-      constraintEqualToAnchor:[titleURLContainer bottomAnchor]
-                     constant:-kShareExtensionPadding]
-      .active = YES;
+  [_titleURLContainer addSubview:_titleLabel];
+  [_titleURLContainer addSubview:_URLLabel];
 
-  [titleURLStack.centerYAnchor
-      constraintEqualToAnchor:titleURLContainer.centerYAnchor]
-      .active = YES;
-  [titleURLStack.centerXAnchor
-      constraintEqualToAnchor:titleURLContainer.centerXAnchor]
-      .active = YES;
-  [titleURLStack.widthAnchor
-      constraintEqualToAnchor:titleURLContainer.widthAnchor]
-      .active = YES;
-  [titleURLStack
-      setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh
-                                      forAxis:UILayoutConstraintAxisVertical];
+  _itemView = [[UIView alloc] init];
+  [_itemView setTranslatesAutoresizingMaskIntoConstraints:NO];
+  [_itemView addSubview:_titleURLContainer];
+  [_itemView addSubview:_screenshotView];
 
-  _itemStack =
-      [[UIStackView alloc] initWithArrangedSubviews:@[ titleURLContainer ]];
-  [_itemStack setAxis:UILayoutConstraintAxisHorizontal];
-  [_itemStack setLayoutMargins:UIEdgeInsetsMake(kShareExtensionPadding,
-                                                kShareExtensionPadding,
-                                                kShareExtensionPadding,
-                                                kShareExtensionPadding)];
-  [_itemStack setLayoutMarginsRelativeArrangement:YES];
-  [_itemStack setSpacing:kShareExtensionPadding];
+  [NSLayoutConstraint activateConstraints:@[
+    [_titleLabel.topAnchor
+        constraintEqualToAnchor:_titleURLContainer.topAnchor],
+    [_URLLabel.topAnchor constraintEqualToAnchor:_titleLabel.bottomAnchor],
+    [_URLLabel.bottomAnchor
+        constraintEqualToAnchor:_titleURLContainer.bottomAnchor],
+    [_titleLabel.trailingAnchor
+        constraintEqualToAnchor:_titleURLContainer.trailingAnchor],
+    [_URLLabel.trailingAnchor
+        constraintEqualToAnchor:_titleURLContainer.trailingAnchor],
+    [_titleLabel.leadingAnchor
+        constraintEqualToAnchor:_titleURLContainer.leadingAnchor],
+    [_URLLabel.leadingAnchor
+        constraintEqualToAnchor:_titleURLContainer.leadingAnchor],
+    [_titleURLContainer.centerYAnchor
+        constraintEqualToAnchor:_itemView.centerYAnchor],
+    [_itemView.heightAnchor
+        constraintGreaterThanOrEqualToAnchor:_titleURLContainer.heightAnchor
+                                  multiplier:1
+                                    constant:2 * kShareExtensionPadding],
+    [_titleURLContainer.leadingAnchor
+        constraintEqualToAnchor:_itemView.leadingAnchor
+                       constant:kShareExtensionPadding],
+    [_screenshotView.trailingAnchor
+        constraintEqualToAnchor:_itemView.trailingAnchor
+                       constant:-kShareExtensionPadding],
+    [_itemView.heightAnchor
+        constraintGreaterThanOrEqualToAnchor:_screenshotView.heightAnchor
+                                  multiplier:1
+                                    constant:2 * kShareExtensionPadding],
+    [_screenshotView.centerYAnchor
+        constraintEqualToAnchor:_itemView.centerYAnchor],
+  ]];
 
-  [_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [_URLLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [_screenshotView setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [titleURLStack setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [_itemStack setTranslatesAutoresizingMaskIntoConstraints:NO];
+  NSLayoutConstraint* titleURLScreenshotConstraint =
+      [_titleURLContainer.trailingAnchor
+          constraintEqualToAnchor:_screenshotView.leadingAnchor];
+  titleURLScreenshotConstraint.priority = UILayoutPriorityDefaultHigh;
+  titleURLScreenshotConstraint.active = YES;
 
-  return _itemStack;
+  return _itemView;
 }
 
 - (UIView*)dividerViewWithVibrancy:(UIVisualEffect*)vibrancyEffect {
@@ -414,9 +424,13 @@
 }
 
 - (void)setScreenshot:(UIImage*)screenshot {
-  [[self screenshotView] setHidden:NO];
+  [self.screenshotView.widthAnchor constraintEqualToConstant:kScreenshotSize]
+      .active = YES;
+  [self.titleURLContainer.trailingAnchor
+      constraintEqualToAnchor:self.screenshotView.leadingAnchor
+                     constant:-kShareExtensionPadding]
+      .active = YES;
   [[self screenshotView] setImage:screenshot];
-  [[self itemStack] addArrangedSubview:[self screenshotView]];
 }
 
 @end
diff --git a/ios/clean/chrome/browser/ui/presenters/BUILD.gn b/ios/clean/chrome/browser/ui/presenters/BUILD.gn
index 99e39183..3a89a33 100644
--- a/ios/clean/chrome/browser/ui/presenters/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/presenters/BUILD.gn
@@ -10,4 +10,8 @@
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//ios/clean/chrome/browser/ui/commands",
+  ]
 }
diff --git a/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.h b/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.h
index 402c92a7..d4d1347 100644
--- a/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.h
+++ b/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.h
@@ -11,14 +11,21 @@
 
 #import <UIKit/UIKit.h>
 
+@protocol ToolbarCommands;
+
 // A presentation controller for presenting a "menu" interface: a (usually)
 // rectangular presentation that covers part of the window, and which doesn't
 // obscure the presenting view. The presenting view remains in the view
-// hierarchy. If the presenting view controller confroms to the
-// MenuPresentationDelegate protocol, that protocol will be used to determine
+// hierarchy.
+// The presented view controller (that is, the view controller that runs the
+// menu) should set the size of its view in -loadView or -viewDidLoad.
+// If the presenting view controller conforms to the MenuPresentationDelegate
+// protocol, that protocol will be used to determine the region the menu
+// appears in; otherwise it will appear in the center of the presenting view
 // the region the menu appears in. Otherwise a default rectangular area is
-// used.
+// controller's view.
 @interface MenuPresentationController : UIPresentationController
+@property(nonatomic, assign) id<ToolbarCommands> toolbarCommandHandler;
 @end
 
 #endif  // IOS_CLEAN_CHROME_BROWSER_UI_PRESENTERS_MENU_PRESENTATION_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.mm b/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.mm
index 39635293..ebba798 100644
--- a/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.mm
+++ b/ios/clean/chrome/browser/ui/presenters/menu_presentation_controller.mm
@@ -10,6 +10,7 @@
 
 #import <QuartzCore/QuartzCore.h>
 
+#include "ios/clean/chrome/browser/ui/commands/toolbar_commands.h"
 #include "ios/clean/chrome/browser/ui/presenters/menu_presentation_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,11 +20,14 @@
 @interface MenuPresentationController ()
 @property(nonatomic, weak) id<MenuPresentationDelegate> presentationDelegate;
 @property(nonatomic, assign) CGRect presentationFrame;
+@property(nonatomic, strong) UITapGestureRecognizer* dismissRecognizer;
 @end
 
 @implementation MenuPresentationController
 @synthesize presentationDelegate = _presentationDelegate;
 @synthesize presentationFrame = _presentationFrame;
+@synthesize toolbarCommandHandler = _toolbarCommandHandler;
+@synthesize dismissRecognizer = _dismissRecognizer;
 
 #pragma mark - UIPresentationDelegate
 
@@ -34,9 +38,12 @@
       self.presentationFrame =
           [self.presentationDelegate frameForMenuPresentation:self];
     } else {
-      // Placeholder default frame: something rectangular, 50 points in and
-      // down.
-      self.presentationFrame = CGRectMake(50, 50, 250, 300);
+      // Placeholder default frame: centered in the presenting view.
+      CGSize menuSize = self.presentedView.frame.size;
+      self.presentationFrame.size = menuSize;
+      self.presentationFrame.origin = CGPointMake(
+          (self.containerView.bounds.size.width - menuSize.width) / 2.0,
+          (self.containerView.bounds.size.height - menuSize.height) / 2.0);
     }
   }
   return self.presentationFrame;
@@ -46,10 +53,19 @@
   self.presentedView.layer.borderWidth = 1.0;
   self.presentedView.layer.shadowRadius = 1.0;
   self.presentedView.layer.borderColor = [UIColor blackColor].CGColor;
+
+  self.dismissRecognizer =
+      [[UITapGestureRecognizer alloc] initWithTarget:self
+                                              action:@selector(tapToDismiss:)];
+  [self.containerView addGestureRecognizer:self.dismissRecognizer];
 }
 
 #pragma mark - Private methods.
 
+- (void)tapToDismiss:(UIGestureRecognizer*)recognizer {
+  [self.toolbarCommandHandler closeToolsMenu];
+}
+
 // Checks if the presenting view controller conforms to
 // MenuPresentationDelegate and, if so, sets that view controller as the
 // presentation delegate. This can't be done at init time, becuase the
diff --git a/ios/clean/chrome/browser/ui/strip/strip_container_view_controller.mm b/ios/clean/chrome/browser/ui/strip/strip_container_view_controller.mm
index b9a05c2..af22de3b 100644
--- a/ios/clean/chrome/browser/ui/strip/strip_container_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/strip/strip_container_view_controller.mm
@@ -8,8 +8,8 @@
 
 #import "ios/clean/chrome/browser/ui/strip/strip_container_view_controller.h"
 
-#import "ios/clean/chrome/browser/ui/actions/tab_strip_actions.h"
 #import "ios/clean/chrome/browser/ui/ui_types.h"
+#import "ios/clean/chrome/browser/ui/actions/tab_strip_actions.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h
index 60e1191f..03a2bf46 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h
+++ b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h
@@ -11,6 +11,7 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/clean/chrome/browser/ui/animators/zoom_transition_delegate.h"
 #import "ios/clean/chrome/browser/ui/presenters/menu_presentation_delegate.h"
 
 // Base class for a view controller that contains a content view (generally
@@ -18,7 +19,7 @@
 // view, each managed by their own view controllers.
 // Subclasses manage the specific layout of these view controllers.
 @interface TabContainerViewController
-    : UIViewController<MenuPresentationDelegate>
+    : UIViewController<MenuPresentationDelegate, ZoomTransitionDelegate>
 
 // View controller showing the main content for the tab. If there is no
 // toolbar view controller set, the contents of this view controller will
diff --git a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
index c494582..1246121 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
@@ -8,6 +8,7 @@
 
 #import "ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h"
 
+#import "base/mac/foundation_util.h"
 #import "ios/clean/chrome/browser/ui/ui_types.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -127,8 +128,47 @@
 #pragma mark - MenuPresentationDelegate
 
 - (CGRect)frameForMenuPresentation:(UIPresentationController*)presentation {
-  // Placeholder.
-  return CGRectMake(50, 50, 250, 300);
+  CGSize menuSize = presentation.presentedView.frame.size;
+  CGRect menuRect;
+  menuRect.size = menuSize;
+
+  CGRect menuOriginRect = [self rectForZoomWithKey:@"" inView:self.view];
+  if (CGRectIsNull(menuOriginRect)) {
+    menuRect.origin = CGPointMake(50, 50);
+    return menuRect;
+  }
+  // Calculate which corner of the menu the origin rect is in. This is
+  // determined by comparing frames, and thus is RTL-independent.
+  if (CGRectGetMinX(menuOriginRect) - CGRectGetMinX(self.view.bounds) <
+      CGRectGetMaxX(self.view.bounds) - CGRectGetMaxX(menuOriginRect)) {
+    // Origin rect is closer to the left edge of |self.view| than to the right.
+    menuRect.origin.x = CGRectGetMinX(menuOriginRect);
+  } else {
+    // Origin rect is closer to the right edge of |self.view| than to the left.
+    menuRect.origin.x = CGRectGetMaxX(menuOriginRect) - menuSize.width;
+  }
+
+  if (CGRectGetMinY(menuOriginRect) - CGRectGetMinY(self.view.bounds) <
+      CGRectGetMaxY(self.view.bounds) - CGRectGetMaxY(menuOriginRect)) {
+    // Origin rect is closer to the top edge of |self.view| than to the bottom.
+    menuRect.origin.y = CGRectGetMinY(menuOriginRect);
+  } else {
+    // Origin rect is closer to the bottom edge of |self.view| than to the top.
+    menuRect.origin.y = CGRectGetMaxY(menuOriginRect) - menuSize.height;
+  }
+
+  return menuRect;
+}
+
+#pragma mark - ZoomTransitionDelegate
+
+- (CGRect)rectForZoomWithKey:(NSObject*)key inView:(UIView*)view {
+  UIViewController<ZoomTransitionDelegate>* delegate =
+      base::mac::ObjCCast<UIViewController<ZoomTransitionDelegate>>(
+          self.toolbarViewController);
+  if (delegate)
+    return [delegate rectForZoomWithKey:key inView:view];
+  return CGRectNull;
 }
 
 #pragma mark - UIResponder
diff --git a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index 6f2a9e7..aba4969d 100644
--- a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -49,6 +49,7 @@
 
 - (void)showToolsMenu {
   ToolsCoordinator* toolsCoordinator = [[ToolsCoordinator alloc] init];
+  toolsCoordinator.toolbarCommandHandler = self;
   [self addChildCoordinator:toolsCoordinator];
   [toolsCoordinator start];
   self.toolsMenuCoordinator = toolsCoordinator;
diff --git a/ios/clean/chrome/browser/ui/tools/menu_view_controller.mm b/ios/clean/chrome/browser/ui/tools/menu_view_controller.mm
index 4272d9a..99d8d9c 100644
--- a/ios/clean/chrome/browser/ui/tools/menu_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/tools/menu_view_controller.mm
@@ -17,25 +17,62 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+const CGFloat kMenuWidth = 250;
+const CGFloat kMenuItemHeight = 44;
+}
+
+// Placeholder model for menu item configuration.
+@interface MenuItem : NSObject
+@property(nonatomic, copy) NSString* title;
+@property(nonatomic) SEL action;
+@end
+
+@implementation MenuItem
+@synthesize title = _title;
+@synthesize action = _action;
+@end
+
+@interface MenuViewController ()
+@property(nonatomic, readonly) NSArray<MenuItem*>* menuItems;
+@end
+
 @implementation MenuViewController
+@synthesize menuItems = _menuItems;
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    _menuItems = @[
+      [[MenuItem alloc] init], [[MenuItem alloc] init], [[MenuItem alloc] init],
+      [[MenuItem alloc] init]
+    ];
+
+    _menuItems[0].title = @"New Tab";
+
+    _menuItems[1].title = @"Find in Page…";
+
+    _menuItems[2].title = @"Request Desktop Site";
+
+    _menuItems[3].title = @"Settings";
+    _menuItems[3].action = @selector(showSettings:);
+  }
+  return self;
+}
+
+- (void)loadView {
+  CGRect frame;
+  frame.size = CGSizeMake(kMenuWidth, kMenuItemHeight * _menuItems.count);
+  frame.origin = CGPointZero;
+  self.view = [[UIView alloc] initWithFrame:frame];
+  self.view.backgroundColor = [UIColor whiteColor];
+  self.view.autoresizingMask = UIViewAutoresizingNone;
+}
 
 - (void)viewDidLoad {
-  self.view.backgroundColor = [UIColor whiteColor];
-  struct MenuItem {
-    NSString* title;
-    SEL action;
-  };
-  MenuItem menuItems[] = {
-      {@"New Tab", nullptr},
-      {@"Find in Page…", nullptr},
-      {@"Request Desktop Site", nullptr},
-      {@"Settings", @selector(showSettings:)},
-  };
   NSMutableArray<UIButton*>* buttons =
-      [[NSMutableArray alloc] initWithCapacity:arraysize(menuItems)];
+      [[NSMutableArray alloc] initWithCapacity:_menuItems.count];
 
-  for (size_t i = 0; i < arraysize(menuItems); ++i) {
-    const MenuItem& item = menuItems[i];
+  for (MenuItem* item in _menuItems) {
     UIButton* menuButton = [UIButton buttonWithType:UIButtonTypeSystem];
     menuButton.translatesAutoresizingMaskIntoConstraints = NO;
     [menuButton setTitle:item.title forState:UIControlStateNormal];
diff --git a/ios/clean/chrome/browser/ui/tools/tools_coordinator.h b/ios/clean/chrome/browser/ui/tools/tools_coordinator.h
index 9230705..a6eed0b 100644
--- a/ios/clean/chrome/browser/ui/tools/tools_coordinator.h
+++ b/ios/clean/chrome/browser/ui/tools/tools_coordinator.h
@@ -13,9 +13,12 @@
 
 #import "ios/clean/chrome/browser/browser_coordinator.h"
 
+@protocol ToolbarCommands;
+
 // Coordinator that shows an inteface for the user to select a
 // tool or action to use.
 @interface ToolsCoordinator : BrowserCoordinator
+@property(nonatomic, assign) id<ToolbarCommands> toolbarCommandHandler;
 @end
 
 #endif  // IOS_CLEAN_CHROME_BROWSER_UI_TOOLS_TOOLS_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
index 50946fd..3d01ac2 100644
--- a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
@@ -21,6 +21,7 @@
 @end
 
 @implementation ToolsCoordinator
+@synthesize toolbarCommandHandler = _toolbarCommandHandler;
 @synthesize menuViewController = _menuViewController;
 
 #pragma mark - BrowserCoordinator
@@ -67,6 +68,7 @@
       [[MenuPresentationController alloc]
           initWithPresentedViewController:presented
                  presentingViewController:presenting];
+  menuPresentation.toolbarCommandHandler = self.toolbarCommandHandler;
   return menuPresentation;
 }
 
diff --git a/ios/web/app/web_main_loop.mm b/ios/web/app/web_main_loop.mm
index d155fbcc..b5e59fc 100644
--- a/ios/web/app/web_main_loop.mm
+++ b/ios/web/app/web_main_loop.mm
@@ -78,7 +78,7 @@
 size_t DefaultBrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
   const bool is_background =
       traits.priority() == base::TaskPriority::BACKGROUND;
-  if (traits.with_file_io())
+  if (traits.may_block() || traits.with_base_sync_primitives())
     return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
 
   return is_background ? BACKGROUND : FOREGROUND;
diff --git a/ios/web/net/request_tracker_impl.h b/ios/web/net/request_tracker_impl.h
index e1695a6..582d615 100644
--- a/ios/web/net/request_tracker_impl.h
+++ b/ios/web/net/request_tracker_impl.h
@@ -284,9 +284,6 @@
   // |load_success| indicates if the page successfully loaded.
   void StopPageLoad(const GURL& url, bool load_success);
 
-  // Cancels all the requests in |live_requests_|.
-  void CancelRequests();
-
 #pragma mark Private Consumer API
   // Private methods that call into delegate methods.
 
@@ -343,8 +340,6 @@
   // progress, and thus requests corresponding to old navigation events are not
   // in it.
   std::map<const void*, TrackerCounts*> counts_by_request_;
-  // All the live requests associated with the tracker.
-  std::set<net::URLRequest*> live_requests_;
   // A list of all the TrackerCounts, including the finished ones.
   std::vector<std::unique_ptr<TrackerCounts>> counts_;
   // The system shall never allow the page load estimate to go back.
diff --git a/ios/web/net/request_tracker_impl.mm b/ios/web/net/request_tracker_impl.mm
index bc0986f9..99939ca 100644
--- a/ios/web/net/request_tracker_impl.mm
+++ b/ios/web/net/request_tracker_impl.mm
@@ -405,7 +405,6 @@
                            base::Bind(
                                [](RequestTrackerImpl* tracker) {
                                  tracker->is_closing_ = true;
-                                 tracker->CancelRequests();
                                },
                                base::RetainedRef(this)));
 
@@ -1240,21 +1239,6 @@
                                     [urls componentsJoinedByString:@"\n"]];
 }
 
-void RequestTrackerImpl::CancelRequests() {
-  DCHECK_CURRENTLY_ON(web::WebThread::IO);
-  std::set<net::URLRequest*>::iterator it;
-  // TODO(droger): When canceling the request, we should in theory make sure
-  // that the NSURLProtocol client method |didFailWithError| is called,
-  // otherwise the iOS system may wait indefinitely for the request to complete.
-  // However, as we currently only cancel the requests when closing a tab, the
-  // requests are all canceled by the system shortly after and nothing bad
-  // happens.
-  for (it = live_requests_.begin(); it != live_requests_.end(); ++it)
-    (*it)->Cancel();
-
-  live_requests_.clear();
-}
-
 void RequestTrackerImpl::SetCertificatePolicyCacheForTest(
     web::CertificatePolicyCache* cache) {
   policy_cache_ = cache;
diff --git a/ios/web/shell/test/context_menu_egtest.mm b/ios/web/shell/test/context_menu_egtest.mm
index 9b10422..cc1d25b 100644
--- a/ios/web/shell/test/context_menu_egtest.mm
+++ b/ios/web/shell/test/context_menu_egtest.mm
@@ -33,7 +33,7 @@
 
 @implementation ContextMenuTestCase
 
-// TODO(crbug.com/675015): Re-enable this test on device.
+// TODO(crbug.com/675399): Re-enable this test on device.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_testContextMenu testContextMenu
 #else
@@ -75,7 +75,7 @@
   [[EarlGrey selectElementWithMatcher:copyItem] assertWithMatcher:grey_nil()];
 }
 
-// TODO(crbug.com/675015): Re-enable this test on device.
+// TODO(crbug.com/675399): Re-enable this test on device.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_testContextMenuWebkitTouchCalloutNone \
   testContextMenuWebkitTouchCalloutNone
@@ -112,7 +112,7 @@
   [[EarlGrey selectElementWithMatcher:copyItem] assertWithMatcher:grey_nil()];
 }
 
-// TODO(crbug.com/675015): Re-enable this test on device.
+// TODO(crbug.com/675399): Re-enable this test on device.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_testContextMenuWebkitTouchCalloutNoneFromAncestor \
   testContextMenuWebkitTouchCalloutNoneFromAncestor
@@ -151,7 +151,7 @@
   [[EarlGrey selectElementWithMatcher:copyItem] assertWithMatcher:grey_nil()];
 }
 
-// TODO(crbug.com/675015): Re-enable this test on device.
+// TODO(crbug.com/675399): Re-enable this test on device.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_testContextMenuWebkitTouchCalloutOverride \
   testContextMenuWebkitTouchCalloutOverride
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc
index b0a4709..b036faca 100644
--- a/ipc/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -18,7 +18,6 @@
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/test/mojo_test_base.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 
 #if defined(OS_POSIX)
 #include "base/file_descriptor_posix.h"
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 4c0990a9..5358b6a 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -20,7 +20,6 @@
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/test/mojo_test_base.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
diff --git a/ipc/run_all_perftests.cc b/ipc/run_all_perftests.cc
index 1e3d91f..b22c912 100644
--- a/ipc/run_all_perftests.cc
+++ b/ipc/run_all_perftests.cc
@@ -8,7 +8,7 @@
 #include "base/test/perf_test_suite.h"
 #include "base/test/test_io_thread.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "mojo/edk/test/test_support_impl.h"
 
 int main(int argc, char** argv) {
@@ -16,7 +16,9 @@
 
   mojo::edk::Init();
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
   mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
 
   return test.Run();
diff --git a/ipc/run_all_unittests.cc b/ipc/run_all_unittests.cc
index 5646c67..cd62e45 100644
--- a/ipc/run_all_unittests.cc
+++ b/ipc/run_all_unittests.cc
@@ -9,7 +9,7 @@
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
 #include "base/mac/mach_port_broker.h"
@@ -19,10 +19,9 @@
   base::TestSuite test_suite(argc, argv);
   mojo::edk::Init();
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
-  // really does nothing in the new EDK but does depend on the current message
-  // loop, which is destructed inside base::LaunchUnitTests.
-  new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
   base::MachPortBroker mach_broker("mojo_test");
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 67a7376e..c652e517 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -15,7 +15,6 @@
     "platform_channel_pair.h",
     "platform_handle.h",
     "platform_handle_utils.h",
-    "process_delegate.h",
     "scoped_platform_handle.h",
   ]
 
@@ -54,7 +53,6 @@
   defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
 
   public_deps = [
-    ":delegates",
     ":headers",
     ":platform",
     "//base",
@@ -121,25 +119,6 @@
   }
 }
 
-source_set("delegates") {
-  # This isn't really a standalone target; it must be linked into the
-  # mojo_system_impl component.
-  visibility = [
-    ":embedder",
-    "//mojo/edk/system",
-  ]
-
-  sources = [
-    "process_delegate.h",
-  ]
-
-  defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
-
-  public_deps = [
-    "//mojo/public/cpp/system",
-  ]
-}
-
 source_set("embedder_unittests") {
   testonly = true
 
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 5cf70e5..41d48d4dc2 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -18,8 +18,8 @@
 #include "mojo/edk/embedder/embedder_internal.h"
 #include "mojo/edk/embedder/entrypoints.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/process_delegate.h"
 #include "mojo/edk/system/core.h"
+#include "mojo/edk/system/node_controller.h"
 
 #if !defined(OS_NACL)
 #include "crypto/random.h"
@@ -34,7 +34,6 @@
 namespace internal {
 
 Core* g_core;
-ProcessDelegate* g_process_delegate;
 
 Core* GetCore() { return g_core; }
 
@@ -83,14 +82,12 @@
 
 ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe,
                                              const std::string& peer_token) {
-  CHECK(internal::g_process_delegate);
   DCHECK(pipe.is_valid());
   DCHECK(!peer_token.empty());
   return internal::g_core->ConnectToPeerProcess(std::move(pipe), peer_token);
 }
 
 void ClosePeerConnection(const std::string& peer_token) {
-  CHECK(internal::g_process_delegate);
   return internal::g_core->ClosePeerConnection(peer_token);
 }
 
@@ -137,19 +134,18 @@
       mojo_handle, shared_memory_handle, num_bytes, read_only);
 }
 
-void InitIPCSupport(ProcessDelegate* process_delegate,
-                    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+void InitIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner) {
   CHECK(internal::g_core);
   internal::g_core->SetIOTaskRunner(io_thread_task_runner);
-  internal::g_process_delegate = process_delegate;
 }
 
-void ShutdownIPCSupport() {
-  CHECK(internal::g_process_delegate);
+scoped_refptr<base::TaskRunner> GetIOTaskRunner() {
+  return internal::g_core->GetNodeController()->io_task_runner();
+}
+
+void ShutdownIPCSupport(const base::Closure& callback) {
   CHECK(internal::g_core);
-  internal::g_core->RequestShutdown(
-      base::Bind(&ProcessDelegate::OnShutdownComplete,
-                 base::Unretained(internal::g_process_delegate)));
+  internal::g_core->RequestShutdown(callback);
 }
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -161,18 +157,15 @@
 
 ScopedMessagePipeHandle CreateMessagePipe(
     ScopedPlatformHandle platform_handle) {
-  CHECK(internal::g_process_delegate);
   return internal::g_core->CreateMessagePipe(std::move(platform_handle));
 }
 
 ScopedMessagePipeHandle CreateParentMessagePipe(
     const std::string& token, const std::string& child_token) {
-  CHECK(internal::g_process_delegate);
   return internal::g_core->CreateParentMessagePipe(token, child_token);
 }
 
 ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token) {
-  CHECK(internal::g_process_delegate);
   return internal::g_core->CreateChildMessagePipe(token);
 }
 
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index e673aff2..c0672aa8 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -27,8 +27,6 @@
 namespace mojo {
 namespace edk {
 
-class ProcessDelegate;
-
 using ProcessErrorCallback = base::Callback<void(const std::string& error)>;
 
 // Basic configuration/initialization ------------------------------------------
@@ -150,22 +148,20 @@
 //
 // This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC
 // functions may be called after this is called.
-
-// Initializes a process of the given type; to be called after |Init()|.
-//   - |process_delegate| must be a process delegate of the appropriate type
-//     corresponding to |process_type|; its methods will be called on the same
-//     thread as Shutdown.
-//   - |process_delegate|, and |io_thread_task_runner| should live at least
-//     until |ShutdownIPCSupport()|'s callback has been run.
+//
+// |io_thread_task_runner| should live at least until |ShutdownIPCSupport()|'s
+// callback has been run.
 MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport(
-    ProcessDelegate* process_delegate,
     scoped_refptr<base::TaskRunner> io_thread_task_runner);
 
+// Retrieves the TaskRunner used for IPC I/O, as set by InitIPCSupport.
+MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner();
+
 // Shuts down the subsystem initialized by |InitIPCSupport()|. It be called from
 // any thread and will attempt to complete shutdown on the I/O thread with which
-// the system was initialized. Upon completion the ProcessDelegate's
-// |OnShutdownComplete()| method is invoked.
-MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport();
+// the system was initialized. Upon completion, |callback| is invoked on an
+// arbitrary thread.
+MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(const base::Closure& callback);
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
 // Set the |base::PortProvider| for this process. Can be called on any thread,
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index 4521f80..f0fe440 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -23,13 +23,13 @@
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_timeouts.h"
+#include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/named_platform_handle.h"
 #include "mojo/edk/embedder/named_platform_handle_utils.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/test_embedder.h"
 #include "mojo/edk/system/test_utils.h"
 #include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "mojo/public/c/system/core.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -595,7 +595,7 @@
   // work. By the time the local message pipe has been observerd as closed,
   // that task will have been posted. Therefore, a task to create the client
   // connection should be handled after the channel is closed.
-  test::GetIoTaskRunner()->PostTaskAndReply(
+  GetIOTaskRunner()->PostTaskAndReply(
       FROM_HERE,
       base::Bind(&CreateClientHandleOnIoThread, named_handle, &client_handle),
       run_loop.QuitClosure());
diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h
deleted file mode 100644
index 144f4a3a..0000000
--- a/mojo/edk/embedder/process_delegate.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
-#define MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
-
-#include "base/macros.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace mojo {
-namespace edk {
-
-// An interface for process delegates.
-class MOJO_SYSTEM_IMPL_EXPORT ProcessDelegate {
- public:
-  // Called when |ShutdownIPCSupport()| has completed work on the I/O thread.
-  virtual void OnShutdownComplete() = 0;
-
- protected:
-  ProcessDelegate() {}
-  virtual ~ProcessDelegate() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProcessDelegate);
-};
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
diff --git a/mojo/edk/embedder/scoped_ipc_support.cc b/mojo/edk/embedder/scoped_ipc_support.cc
index 9c598b5..f67210a8 100644
--- a/mojo/edk/embedder/scoped_ipc_support.cc
+++ b/mojo/edk/embedder/scoped_ipc_support.cc
@@ -4,57 +4,35 @@
 
 #include "mojo/edk/embedder/scoped_ipc_support.h"
 
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/macros.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/process_delegate.h"
 
 namespace mojo {
 namespace edk {
 
-namespace {
-class IPCSupportInitializer : public mojo::edk::ProcessDelegate {
- public:
-  IPCSupportInitializer() {}
-  ~IPCSupportInitializer() override {}
-
-  void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner) {
-    CHECK(!io_thread_task_runner_);
-    CHECK(io_thread_task_runner);
-    io_thread_task_runner_ = io_thread_task_runner;
-
-    mojo::edk::InitIPCSupport(this, io_thread_task_runner_);
-  }
-
-  void ShutDown() {
-    CHECK(io_thread_task_runner_);
-    mojo::edk::ShutdownIPCSupport();
-  }
-
- private:
-  // mojo::edk::ProcessDelegate:
-  void OnShutdownComplete() override {
-    // TODO(rockot): We should ensure that IO runner shutdown is blocked until
-    // this is called.
-  }
-
-  scoped_refptr<base::TaskRunner> io_thread_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(IPCSupportInitializer);
-};
-
-base::LazyInstance<IPCSupportInitializer>::Leaky ipc_support_initializer;
-
-}  // namespace
-
 ScopedIPCSupport::ScopedIPCSupport(
-    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
-  ipc_support_initializer.Get().Init(io_thread_task_runner);
+    scoped_refptr<base::TaskRunner> io_thread_task_runner,
+    ShutdownPolicy shutdown_policy) : shutdown_policy_(shutdown_policy) {
+  InitIPCSupport(io_thread_task_runner);
 }
 
 ScopedIPCSupport::~ScopedIPCSupport() {
-  ipc_support_initializer.Get().ShutDown();
+  if (shutdown_policy_ == ShutdownPolicy::FAST) {
+    ShutdownIPCSupport(base::Bind(&base::DoNothing));
+    return;
+  }
+
+  base::WaitableEvent shutdown_event(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  ShutdownIPCSupport(base::Bind(&base::WaitableEvent::Signal,
+                                base::Unretained(&shutdown_event)));
+
+  base::ThreadRestrictions::ScopedAllowWait allow_io;
+  shutdown_event.Wait();
 }
 
 }  // namespace edk
diff --git a/mojo/edk/embedder/scoped_ipc_support.h b/mojo/edk/embedder/scoped_ipc_support.h
index a3b32da3..22d8e50 100644
--- a/mojo/edk/embedder/scoped_ipc_support.h
+++ b/mojo/edk/embedder/scoped_ipc_support.h
@@ -5,26 +5,110 @@
 #ifndef MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_
 #define MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_
 
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
 #include "mojo/edk/system/system_impl_export.h"
 
+namespace base {
+class TaskRunner;
+}
+
 namespace mojo {
 namespace edk {
 
-// Performs any necessary Mojo initialization on construction, and shuts
-// down Mojo on destruction.
-//
-// This should be instantiated once per process and retained as long as Mojo
-// is needed. The TaskRunner passed to the constructor should outlive this
-// object.
+// A simple class that calls |InitIPCSupport()| on construction and
+// |ShutdownIPCSupport()| on destruction, blocking the destructor on clean IPC
+// shutdown completion.
 class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport {
  public:
-  ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner);
+  // ShutdownPolicy is a type for specifying the desired Mojo IPC support
+  // shutdown behavior used during ScopedIPCSupport destruction.
+  //
+  // What follows is a quick overview of why shutdown behavior is interesting
+  // and how you might decide which behavior is right for your use case.
+  //
+  // BACKGROUND
+  // ==========
+  //
+  // In order to facilitate efficient and reliable transfer of Mojo message pipe
+  // endpoints across process boundaries, the underlying model for a message
+  // pipe is actually a self-collapsing cycle of "ports." See
+  // //mojo/edk/system/ports for gritty implementation details.
+  //
+  // Ports are essentially globally unique identifiers used for system-wide
+  // message routing. Every message pipe consists of at least two such ports:
+  // the pipe's two concrete endpoints.
+  //
+  // When a message pipe endpoint is transferred over another message pipe, that
+  // endpoint's port (which subsequently exists only internally with no
+  // publicly-reachable handle) enters a transient proxying state for the
+  // remainder of its lifetime. Once sufficient information has been
+  // proagated throughout the system and this proxying port can be safely
+  // bypassed, it is garbage-collected.
+  //
+  // If a process is terminated while hosting any active proxy ports, this
+  // will necessarily break the message pipe(s) to which those ports belong.
+  //
+  // WHEN TO USE CLEAN SHUTDOWN
+  // ==========================
+  //
+  // Consider three processes, A, B, and C. Suppose A creates a message pipe,
+  // sending one end to B and the other to C. For some brief period of time,
+  // messages sent by B or C over this pipe may be proxied through A.
+  //
+  // If A is suddenly terminated, there may be no way for B's messages to reach
+  // C (and vice versa), since the message pipe state may not have been fully
+  // propagated to all concerned processes in the system. As such, both B and C
+  // may have no choice but to signal peer closure on their respective ends of
+  // the pipe, and thus the pipe may be broken despite a lack of intent by
+  // either B or C.
+  //
+  // This can also happen if A creates a pipe and passes one end to B, who then
+  // passes it along to C. B may temporarily proxy messages for this pipe
+  // between A and C, and B's sudden demise will in turn beget the pipe's
+  // own sudden demise.
+  //
+  // In situations where these sort of arrangements may occur, potentially
+  // proxying processes must ensure they are shut down cleanly in order to avoid
+  // flaky system behavior.
+  //
+  // WHEN TO USE FAST SHUTDOWN
+  // =========================
+  //
+  // As a general rule of thumb, if your process never creates a message pipe
+  // where both ends are passed to other processes, or never forwards a pipe
+  // endpoint from one process to another, fast shutdown is safe. Satisfaction
+  // of these constraints can be difficult to prove though, so clean shutdown is
+  // a safe default choice.
+  //
+  // Content renderer processes are a good example of a case where fast shutdown
+  // is safe, because as a matter of security and stability, a renderer cannot
+  // be trusted to do any proxying on behalf of two other processes anyway.
+  //
+  // There are other practical scenarios where fast shutdown is safe even if
+  // the process may have live proxies. For example, content's browser process
+  // is treated as a sort of master process in the system, in the sense that if
+  // the browser is terminated, no other part of the system is expected to
+  // continue normal operation anyway. In this case the side-effects of fast
+  // shutdown are irrelevant, so fast shutdown is preferred.
+  enum class ShutdownPolicy {
+    // Clean shutdown. This causes the ScopedIPCSupport destructor to *block*
+    // the calling thread until clean shutdown is complete. See explanation
+    // above for details.
+    CLEAN,
+
+    // Fast shutdown. In this case a cheap best-effort attempt is made to
+    // shut down the IPC system, but no effort is made to wait for its
+    // completion. See explanation above for details.
+    FAST,
+  };
+
+  ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner,
+                   ShutdownPolicy shutdown_policy);
   ~ScopedIPCSupport();
 
  private:
+  const ShutdownPolicy shutdown_policy_;
+
   DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport);
 };
 
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index 9023fbd..c24458a 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -78,7 +78,6 @@
 
   public_deps = [
     "//mojo/edk/embedder",
-    "//mojo/edk/embedder:delegates",
     "//mojo/edk/embedder:platform",
     "//mojo/edk/system/ports",
     "//mojo/public/c/system",
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index cfcb777..308c2879 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -332,15 +332,7 @@
 }
 
 void Core::RequestShutdown(const base::Closure& callback) {
-  base::Closure on_shutdown;
-  if (base::ThreadTaskRunnerHandle::IsSet()) {
-    on_shutdown = base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
-                             base::ThreadTaskRunnerHandle::Get(),
-                             FROM_HERE, callback);
-  } else {
-    on_shutdown = callback;
-  }
-  GetNodeController()->RequestShutdown(on_shutdown);
+  GetNodeController()->RequestShutdown(callback);
 }
 
 ScopedMessagePipeHandle Core::CreateMessagePipe(
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index ebacc64..a15456a 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -9,8 +9,6 @@
   sources = [
     "mojo_test_base.cc",
     "mojo_test_base.h",
-    "scoped_ipc_support.cc",
-    "scoped_ipc_support.h",
     "test_utils.h",
     "test_utils_posix.cc",
     "test_utils_win.cc",
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
index 8f2511f..3ce3b47 100644
--- a/mojo/edk/test/run_all_perftests.cc
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -7,8 +7,8 @@
 #include "base/test/perf_test_suite.h"
 #include "base/test/test_io_thread.h"
 #include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "mojo/edk/test/test_support_impl.h"
 #include "mojo/public/tests/test_support_private.h"
 
@@ -17,7 +17,9 @@
 
   mojo::edk::Init();
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
   mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
 
   return test.Run();
diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc
index 0b0663b..a057825 100644
--- a/mojo/edk/test/run_all_unittests.cc
+++ b/mojo/edk/test/run_all_unittests.cc
@@ -11,8 +11,8 @@
 #include "base/test/test_io_thread.h"
 #include "base/test/test_suite.h"
 #include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "mojo/edk/test/test_support_impl.h"
 #include "mojo/public/tests/test_support_private.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -40,7 +40,9 @@
   mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
 
-  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      test_io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
   return base::LaunchUnitTests(
       argc, argv,
       base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
diff --git a/mojo/edk/test/scoped_ipc_support.cc b/mojo/edk/test/scoped_ipc_support.cc
deleted file mode 100644
index 588039a..0000000
--- a/mojo/edk/test/scoped_ipc_support.cc
+++ /dev/null
@@ -1,62 +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 "mojo/edk/test/scoped_ipc_support.h"
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/edk/embedder/embedder.h"
-
-namespace mojo {
-namespace edk {
-namespace test {
-
-namespace {
-base::TaskRunner* g_io_task_runner = nullptr;
-}
-
-base::TaskRunner* GetIoTaskRunner() {
-  return g_io_task_runner;
-}
-
-ScopedIPCSupport::ScopedIPCSupport(
-    scoped_refptr<base::TaskRunner> io_thread_task_runner)
-    : shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
-                      base::WaitableEvent::InitialState::NOT_SIGNALED) {
-  g_io_task_runner = io_thread_task_runner.get();
-  InitIPCSupport(this, io_thread_task_runner);
-}
-
-ScopedIPCSupport::~ScopedIPCSupport() {
-  // ShutdownIPCSupport always runs OnShutdownComplete on the current
-  // ThreadTaskRunnerHandle if set. Otherwise it's run on the IPC thread. We
-  // account for both possibilities here to avoid unnecessarily starting a new
-  // MessageLoop or blocking the existing one.
-  //
-  // TODO(rockot): Clean this up. ShutdownIPCSupport should probably always call
-  // call OnShutdownComplete from the IPC thread.
-  ShutdownIPCSupport();
-  if (base::ThreadTaskRunnerHandle::IsSet()) {
-    base::RunLoop run_loop;
-    shutdown_closure_ = base::Bind(IgnoreResult(&base::TaskRunner::PostTask),
-                                   base::ThreadTaskRunnerHandle::Get(),
-                                   FROM_HERE, run_loop.QuitClosure());
-    run_loop.Run();
-  } else {
-    shutdown_event_.Wait();
-  }
-}
-
-void ScopedIPCSupport::OnShutdownComplete() {
-  if (!shutdown_closure_.is_null())
-    shutdown_closure_.Run();
-  else
-    shutdown_event_.Signal();
-}
-
-}  // namespace test
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/test/scoped_ipc_support.h b/mojo/edk/test/scoped_ipc_support.h
deleted file mode 100644
index c2e5735..0000000
--- a/mojo/edk/test/scoped_ipc_support.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
-#define MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task_runner.h"
-#include "mojo/edk/embedder/process_delegate.h"
-
-namespace mojo {
-namespace edk {
-namespace test {
-
-base::TaskRunner* GetIoTaskRunner();
-
-// A simple class that calls |InitIPCSupport()| on construction and
-// |ShutdownIPCSupport()| on destruction.
-class ScopedIPCSupport : public ProcessDelegate {
- public:
-  explicit ScopedIPCSupport(
-      scoped_refptr<base::TaskRunner> io_thread_task_runner);
-  ~ScopedIPCSupport() override;
-
- private:
-  // |ProcessDelegate| implementation:
-  // Note: Executed on the I/O thread.
-  void OnShutdownComplete() override;
-
-  base::Closure shutdown_closure_;
-  base::WaitableEvent shutdown_event_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport);
-};
-
-}  // namespace test
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index b2e75ae..3d4f8af 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -121,6 +121,7 @@
 
   deps = [
     "//base/test:test_support",
+    "//mojo/edk/system",
     "//mojo/edk/test:test_support",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
diff --git a/mojo/public/cpp/bindings/tests/e2e_perftest.cc b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
index 7b3954f..bc69e0f7 100644
--- a/mojo/public/cpp/bindings/tests/e2e_perftest.cc
+++ b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
@@ -13,8 +13,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/perf_time_logger.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -171,7 +171,7 @@
   InterfaceRequest<test::EchoService> request;
   request.Bind(ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
   base::RunLoop run_loop;
-  edk::test::GetIoTaskRunner()->PostTask(
+  edk::GetIOTaskRunner()->PostTask(
       FROM_HERE,
       base::Bind(&CreateAndRunService, base::Passed(&request),
                  base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
@@ -195,7 +195,7 @@
     MojoHandle client_mp, service_mp;
     CreateMessagePipe(&client_mp, &service_mp);
     WriteMessageWithHandles(mp, "hello", &service_mp, 1);
-    RunTestOnTaskRunner(edk::test::GetIoTaskRunner(), client_mp,
+    RunTestOnTaskRunner(edk::GetIOTaskRunner().get(), client_mp,
                         "MultiProcessEchoIoThread");
   END_CHILD()
 }
diff --git a/net/cert/nss_cert_database.cc b/net/cert/nss_cert_database.cc
index 6842643..d94030c9 100644
--- a/net/cert/nss_cert_database.cc
+++ b/net/cert/nss_cert_database.cc
@@ -176,15 +176,15 @@
   }
 }
 
-int NSSCertDatabase::ImportFromPKCS12(CryptoModule* module,
+int NSSCertDatabase::ImportFromPKCS12(PK11SlotInfo* slot_info,
                                       const std::string& data,
                                       const base::string16& password,
                                       bool is_extractable,
                                       CertificateList* imported_certs) {
   DVLOG(1) << __func__ << " "
-           << PK11_GetModuleID(module->os_module_handle()) << ":"
-           << PK11_GetSlotID(module->os_module_handle());
-  int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
+           << PK11_GetModuleID(slot_info) << ":"
+           << PK11_GetSlotID(slot_info);
+  int result = psm::nsPKCS12Blob_Import(slot_info,
                                         data.data(), data.size(),
                                         password,
                                         is_extractable,
diff --git a/net/cert/nss_cert_database.h b/net/cert/nss_cert_database.h
index 2ee859d..b168f8f19 100644
--- a/net/cert/nss_cert_database.h
+++ b/net/cert/nss_cert_database.h
@@ -168,7 +168,7 @@
   // Returns OK or a network error code such as ERR_PKCS12_IMPORT_BAD_PASSWORD
   // or ERR_PKCS12_IMPORT_ERROR. |imported_certs|, if non-NULL, returns a list
   // of certs that were imported.
-  int ImportFromPKCS12(CryptoModule* module,
+  int ImportFromPKCS12(PK11SlotInfo* slot_info,
                        const std::string& data,
                        const base::string16& password,
                        bool is_extractable,
diff --git a/net/cert/nss_cert_database_unittest.cc b/net/cert/nss_cert_database_unittest.cc
index 9832289..235ea44c 100644
--- a/net/cert/nss_cert_database_unittest.cc
+++ b/net/cert/nss_cert_database_unittest.cc
@@ -68,7 +68,7 @@
             PK11_ReferenceSlot(test_nssdb_.slot())) /* public slot */,
         crypto::ScopedPK11Slot(
             PK11_ReferenceSlot(test_nssdb_.slot())) /* private slot */));
-    public_module_ = cert_db_->GetPublicModule();
+    public_slot_ = cert_db_->GetPublicSlot();
 
     // Test db should be empty at start of test.
     EXPECT_EQ(0U, ListCerts().size());
@@ -82,7 +82,7 @@
   }
 
  protected:
-  CryptoModule* GetPublicModule() { return public_module_.get(); }
+  PK11SlotInfo* GetPublicSlot() { return public_slot_.get(); }
 
   static std::string ReadTestFile(const std::string& name) {
     std::string result;
@@ -128,7 +128,7 @@
   std::unique_ptr<NSSCertDatabase> cert_db_;
   const CertificateList empty_cert_list_;
   crypto::ScopedTestNSSDB test_nssdb_;
-  scoped_refptr<CryptoModule> public_module_;
+  crypto::ScopedPK11Slot public_slot_;
 };
 
 TEST_F(CertDatabaseNSSTest, ListCertsSync) {
@@ -160,7 +160,7 @@
   std::string pkcs12_data = ReadTestFile("client.p12");
 
   EXPECT_EQ(ERR_PKCS12_IMPORT_BAD_PASSWORD,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        base::string16(),
                                        true,  // is_extractable
@@ -174,7 +174,7 @@
   std::string pkcs12_data = ReadTestFile("client.p12");
 
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        true,  // is_extractable
@@ -199,7 +199,7 @@
   std::string pkcs12_data = ReadTestFile("client.p12");
 
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        true,  // is_extractable
@@ -209,7 +209,7 @@
   // NSS has a SEC_ERROR_PKCS12_DUPLICATE_DATA error, but it doesn't look like
   // it's ever used.  This test verifies that.
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        true,  // is_extractable
@@ -221,7 +221,7 @@
   std::string pkcs12_data = ReadTestFile("client.p12");
 
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        false,  // is_extractable
@@ -244,7 +244,7 @@
 TEST_F(CertDatabaseNSSTest, ImportFromPKCS12OnlyMarkIncludedKey) {
   std::string pkcs12_data = ReadTestFile("client.p12");
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        true,  // is_extractable
@@ -256,7 +256,7 @@
   // Now import a PKCS#12 file with just a certificate but no private key.
   pkcs12_data = ReadTestFile("client-nokey.p12");
   EXPECT_EQ(OK,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        ASCIIToUTF16("12345"),
                                        false,  // is_extractable
@@ -276,7 +276,7 @@
   std::string pkcs12_data = "Foobarbaz";
 
   EXPECT_EQ(ERR_PKCS12_IMPORT_INVALID_FILE,
-            cert_db_->ImportFromPKCS12(GetPublicModule(),
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
                                        pkcs12_data,
                                        base::string16(),
                                        true,  // is_extractable
@@ -289,20 +289,24 @@
 TEST_F(CertDatabaseNSSTest, ImportFromPKCS12EmptyPassword) {
   std::string pkcs12_data = ReadTestFile("client-empty-password.p12");
 
-  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(GetPublicModule(), pkcs12_data,
-                                           base::string16(),
-                                           true,  // is_extractable
-                                           NULL));
+  EXPECT_EQ(OK,
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
+                                       pkcs12_data,
+                                       base::string16(),
+                                       true,  // is_extractable
+                                       NULL));
   EXPECT_EQ(1U, ListCerts().size());
 }
 
 TEST_F(CertDatabaseNSSTest, ImportFromPKCS12NullPassword) {
   std::string pkcs12_data = ReadTestFile("client-null-password.p12");
 
-  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(GetPublicModule(), pkcs12_data,
-                                           base::string16(),
-                                           true,  // is_extractable
-                                           NULL));
+  EXPECT_EQ(OK,
+            cert_db_->ImportFromPKCS12(GetPublicSlot(),
+                                       pkcs12_data,
+                                       base::string16(),
+                                       true,  // is_extractable
+                                       NULL));
   EXPECT_EQ(1U, ListCerts().size());
 }
 
diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc
index 1d0649b..ee6a99a 100644
--- a/remoting/host/daemon_process_win.cc
+++ b/remoting/host/daemon_process_win.cc
@@ -128,7 +128,8 @@
     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
     const base::Closure& stopped_callback)
     : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback),
-      ipc_support_(io_task_runner->task_runner()) {}
+      ipc_support_(io_task_runner->task_runner(),
+                   mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST) {}
 
 DaemonProcessWin::~DaemonProcessWin() {
 }
diff --git a/remoting/host/desktop_process_main.cc b/remoting/host/desktop_process_main.cc
index 05743efd..e5b98fc 100644
--- a/remoting/host/desktop_process_main.cc
+++ b/remoting/host/desktop_process_main.cc
@@ -52,7 +52,9 @@
       AutoThread::CreateWithType("I/O thread", ui_task_runner,
                                  base::MessageLoop::TYPE_IO);
 
-  mojo::edk::ScopedIPCSupport ipc_support(io_task_runner->task_runner());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      io_task_runner->task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
   mojo::edk::ScopedPlatformHandle parent_pipe =
       mojo::edk::PlatformChannelPair::PassClientHandleFromParentProcess(
           *command_line);
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index aca4bc9..abda2693 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -470,7 +470,8 @@
   // AutoThreadTaskRunner should not be passed to it. Otherwise, the process may
   // never shut down cleanly.
   ipc_support_ = base::MakeUnique<mojo::edk::ScopedIPCSupport>(
-      context_->network_task_runner()->task_runner());
+      context_->network_task_runner()->task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
   mojo::edk::SetParentPipeHandle(
       mojo::edk::PlatformChannelPair::PassClientHandleFromParentProcess(
           *cmd_line));
diff --git a/remoting/host/security_key/remote_security_key_main.cc b/remoting/host/security_key/remote_security_key_main.cc
index 1490b42..a84012c 100644
--- a/remoting/host/security_key/remote_security_key_main.cc
+++ b/remoting/host/security_key/remote_security_key_main.cc
@@ -141,7 +141,9 @@
 #endif
 
   mojo::edk::Init();
-  mojo::edk::ScopedIPCSupport ipc_support(base::ThreadTaskRunnerHandle::Get());
+  mojo::edk::ScopedIPCSupport ipc_support(
+      base::ThreadTaskRunnerHandle::Get(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST);
 
   base::RunLoop run_loop;
 
diff --git a/remoting/host/security_key/security_key_auth_handler_win_unittest.cc b/remoting/host/security_key/security_key_auth_handler_win_unittest.cc
index dbfe9250..58ad21c 100644
--- a/remoting/host/security_key/security_key_auth_handler_win_unittest.cc
+++ b/remoting/host/security_key/security_key_auth_handler_win_unittest.cc
@@ -17,7 +17,7 @@
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_message_macros.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "remoting/host/host_mock_objects.h"
 #include "remoting/host/security_key/fake_security_key_ipc_client.h"
 #include "remoting/host/security_key/fake_security_key_ipc_server.h"
@@ -88,7 +88,7 @@
   // IPC tests require a valid MessageLoop to run.
   base::MessageLoopForIO message_loop_;
 
-  mojo::edk::test::ScopedIPCSupport ipc_support_;
+  mojo::edk::ScopedIPCSupport ipc_support_;
 
   // Used to allow |message_loop_| to run during tests.  The instance is reset
   // after each stage of the tests has been completed.
@@ -116,7 +116,8 @@
 };
 
 SecurityKeyAuthHandlerWinTest::SecurityKeyAuthHandlerWinTest()
-    : ipc_support_(message_loop_.task_runner()),
+    : ipc_support_(message_loop_.task_runner(),
+                   mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST),
       run_loop_(new base::RunLoop()) {
   auth_handler_ = remoting::SecurityKeyAuthHandler::Create(
       &mock_client_session_details_,
diff --git a/remoting/host/security_key/security_key_ipc_client_unittest.cc b/remoting/host/security_key/security_key_ipc_client_unittest.cc
index cf62fa31..ad9e5f3 100644
--- a/remoting/host/security_key/security_key_ipc_client_unittest.cc
+++ b/remoting/host/security_key/security_key_ipc_client_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/run_loop.h"
 #include "ipc/ipc_channel.h"
 #include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "remoting/host/security_key/fake_security_key_ipc_server.h"
 #include "remoting/host/security_key/security_key_ipc_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -74,7 +74,7 @@
   // IPC tests require a valid MessageLoop to run.
   base::MessageLoopForIO message_loop_;
 
-  mojo::edk::test::ScopedIPCSupport ipc_support_;
+  mojo::edk::ScopedIPCSupport ipc_support_;
 
   // Used to allow |message_loop_| to run during tests.  The instance is reset
   // after each stage of the tests has been completed.
@@ -110,7 +110,8 @@
 };
 
 SecurityKeyIpcClientTest::SecurityKeyIpcClientTest()
-    : ipc_support_(message_loop_.task_runner()),
+    : ipc_support_(message_loop_.task_runner(),
+                   mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST),
       run_loop_(new base::RunLoop()),
       fake_ipc_server_(
           kTestConnectionId,
diff --git a/remoting/host/security_key/security_key_ipc_server_unittest.cc b/remoting/host/security_key/security_key_ipc_server_unittest.cc
index 4d5eb47..075b27e 100644
--- a/remoting/host/security_key/security_key_ipc_server_unittest.cc
+++ b/remoting/host/security_key/security_key_ipc_server_unittest.cc
@@ -16,7 +16,7 @@
 #include "ipc/ipc_channel.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "remoting/host/client_session_details.h"
 #include "remoting/host/security_key/fake_security_key_ipc_client.h"
 #include "remoting/host/security_key/security_key_ipc_constants.h"
@@ -59,7 +59,7 @@
   // IPC tests require a valid MessageLoop to run.
   base::MessageLoopForIO message_loop_;
 
-  mojo::edk::test::ScopedIPCSupport ipc_support_;
+  mojo::edk::ScopedIPCSupport ipc_support_;
 
   // Used to allow |message_loop_| to run during tests.  The instance is reset
   // after each stage of the tests has been completed.
@@ -82,7 +82,8 @@
 };
 
 SecurityKeyIpcServerTest::SecurityKeyIpcServerTest()
-    : ipc_support_(message_loop_.task_runner()),
+    : ipc_support_(message_loop_.task_runner(),
+                   mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST),
       run_loop_(new base::RunLoop()) {
 #if defined(OS_WIN)
   EXPECT_TRUE(ProcessIdToSessionId(
diff --git a/services/service_manager/public/cpp/standalone_service/standalone_service.cc b/services/service_manager/public/cpp/standalone_service/standalone_service.cc
index 2e49b73b..bd26da0 100644
--- a/services/service_manager/public/cpp/standalone_service/standalone_service.cc
+++ b/services/service_manager/public/cpp/standalone_service/standalone_service.cc
@@ -11,7 +11,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/runner/common/client_util.h"
@@ -54,41 +54,6 @@
 }
 #endif
 
-// Should be created and initialized on the main thread and kept alive as long
-// a Service is running in the current process.
-class ScopedAppContext : public mojo::edk::ProcessDelegate {
- public:
-  ScopedAppContext()
-      : io_thread_("io_thread"),
-        wait_for_shutdown_event_(
-            base::WaitableEvent::ResetPolicy::MANUAL,
-            base::WaitableEvent::InitialState::NOT_SIGNALED) {
-    mojo::edk::Init();
-    io_thread_.StartWithOptions(
-        base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-    mojo::edk::InitIPCSupport(this, io_thread_.task_runner());
-    mojo::edk::SetParentPipeHandleFromCommandLine();
-  }
-
-  ~ScopedAppContext() override {
-    mojo::edk::ShutdownIPCSupport();
-    wait_for_shutdown_event_.Wait();
-  }
-
- private:
-  // ProcessDelegate implementation.
-  void OnShutdownComplete() override {
-    wait_for_shutdown_event_.Signal();
-  }
-
-  base::Thread io_thread_;
-
-  // Used to unblock the main thread on shutdown.
-  base::WaitableEvent wait_for_shutdown_event_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedAppContext);
-};
-
 }  // namespace
 
 void RunStandaloneService(const StandaloneServiceCallback& callback) {
@@ -112,7 +77,17 @@
     sandbox = InitializeSandbox();
 #endif
 
-  ScopedAppContext app_context;
+  mojo::edk::Init();
+
+  base::Thread io_thread("io_thread");
+  io_thread.StartWithOptions(
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+
+  mojo::edk::ScopedIPCSupport ipc_support(
+      io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+  mojo::edk::SetParentPipeHandleFromCommandLine();
+
   callback.Run(GetServiceRequestFromCommandLine());
 }
 
diff --git a/services/service_manager/runner/host/service_process_launcher_unittest.cc b/services/service_manager/runner/host/service_process_launcher_unittest.cc
index 5fb3827..cb6fd54c 100644
--- a/services/service_manager/runner/host/service_process_launcher_unittest.cc
+++ b/services/service_manager/runner/host/service_process_launcher_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/run_loop.h"
 #include "base/threading/thread.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace service_manager {
@@ -40,16 +40,6 @@
   callback.Run();
 }
 
-class ProcessDelegate : public mojo::edk::ProcessDelegate {
- public:
-  ProcessDelegate() {}
-  ~ProcessDelegate() override {}
-
- private:
-  void OnShutdownComplete() override {}
-  DISALLOW_COPY_AND_ASSIGN(ProcessDelegate);
-};
-
 class ServiceProcessLauncherDelegateImpl
     : public ServiceProcessLauncher::Delegate {
  public:
@@ -94,8 +84,9 @@
   options.message_loop_type = base::MessageLoop::TYPE_IO;
   io_thread.StartWithOptions(options);
 
-  ProcessDelegate delegate;
-  mojo::edk::InitIPCSupport(&delegate, io_thread.task_runner());
+  auto ipc_support = base::MakeUnique<mojo::edk::ScopedIPCSupport>(
+      io_thread.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
 
   base::FilePath test_service_path =
       base::FilePath(kPackagesPath).AppendASCII(kTestServiceName)
@@ -114,7 +105,8 @@
 
   launcher.Join();
   blocking_pool->Shutdown();
-  mojo::edk::ShutdownIPCSupport();
+  ipc_support.reset();
+
   EXPECT_EQ(1u, service_process_launcher_delegate.get_and_clear_adjust_count());
 }
 
diff --git a/services/service_manager/standalone/context.cc b/services/service_manager/standalone/context.cc
index 4ab116c..469af658 100644
--- a/services/service_manager/standalone/context.cc
+++ b/services/service_manager/standalone/context.cc
@@ -149,7 +149,7 @@
 
   init_edk_ = !init_params || init_params->init_edk;
   if (init_edk_) {
-    mojo::edk::InitIPCSupport(this, io_thread_->task_runner().get());
+    mojo::edk::InitIPCSupport(io_thread_->task_runner().get());
 #if defined(OS_MACOSX)
     mojo::edk::SetMachPortProvider(MachBroker::GetInstance()->port_provider());
 #endif
@@ -243,9 +243,11 @@
     return;
 
   TRACE_EVENT0("service_manager", "Context::Shutdown");
-  // Post a task in case OnShutdownComplete is called synchronously.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(mojo::edk::ShutdownIPCSupport));
+  mojo::edk::ShutdownIPCSupport(
+      base::Bind(IgnoreResult(&base::TaskRunner::PostTask),
+                 base::ThreadTaskRunnerHandle::Get(), FROM_HERE,
+                 base::Bind(&Context::OnShutdownComplete,
+                            base::Unretained(this))));
   // We'll quit when we get OnShutdownComplete().
   base::RunLoop().Run();
 }
diff --git a/services/service_manager/standalone/context.h b/services/service_manager/standalone/context.h
index 17f52b6..e9d9aa6e 100644
--- a/services/service_manager/standalone/context.h
+++ b/services/service_manager/standalone/context.h
@@ -13,7 +13,6 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "mojo/edk/embedder/process_delegate.h"
 #include "services/service_manager/runner/host/service_process_launcher.h"
 #include "services/service_manager/service_manager.h"
 #include "services/service_manager/standalone/tracer.h"
@@ -32,7 +31,7 @@
 constexpr size_t kThreadPoolMaxThreads = 3;
 
 // The "global" context for the service manager's main process.
-class Context : public mojo::edk::ProcessDelegate {
+class Context {
  public:
   struct InitParams {
     InitParams();
@@ -46,7 +45,7 @@
   };
 
   Context();
-  ~Context() override;
+  ~Context();
 
   static void EnsureEmbedderIsInitialized();
 
@@ -63,8 +62,7 @@
   ServiceManager* service_manager() { return service_manager_.get(); }
 
  private:
-  // mojo::edk::ProcessDelegate:
-  void OnShutdownComplete() override;
+  void OnShutdownComplete();
 
   // Runs the app specified by |name|.
   void Run(const std::string& name);
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 889b4773..145cea5 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -6282,19 +6282,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:679e",
-              "os": "Mac-10.10"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -6626,19 +6613,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -6983,19 +6957,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:679e",
-              "os": "Mac-10.10"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -7355,19 +7316,6 @@
         "use_xvfb": false
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -7745,20 +7693,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -8107,20 +8041,6 @@
         "use_xvfb": false
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -8522,20 +8442,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -8935,24 +8841,6 @@
         "use_xvfb": false
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12"
-            },
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -9385,20 +9273,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -9747,20 +9621,6 @@
         "use_xvfb": false
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -10214,19 +10074,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10294,20 +10141,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10376,20 +10209,6 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:0fe9",
-              "hidpi": "1",
-              "os": "Mac"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
index 750c2c4..8231a0f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
@@ -30,9 +30,6 @@
 #  Encoded bytes received & encoded data length mismatch.
 crbug.com/551000 http/tests/inspector/network/network-datareceived.html [ Failure ]
 crbug.com/551000 virtual/mojo-loading/http/tests/inspector/network/network-datareceived.html [ Failure ]
-#  We don't set the right caching policy in the renderer when DevTools overrides the caching policy in reloads.
-crbug.com/551000 http/tests/inspector-protocol/reload-memory-cache.html [ Failure ]
-crbug.com/551000 virtual/mojo-loading/http/tests/inspector-protocol/reload-memory-cache.html [ Failure ]
 #  Console error messages are wrongly ordered.
 crbug.com/551000 http/tests/inspector/console-resource-errors.html [ Failure ]
 crbug.com/551000 virtual/mojo-loading/http/tests/inspector/console-resource-errors.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index dcdf36f..a440d4d 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -92,10 +92,6 @@
 crbug.com/611232 virtual/mojo-loading/http/tests/security/contentSecurityPolicy/frame-src-child-frame-navigates-to-blocked-origin.html [ Timeout ]
 crbug.com/611232 http/tests/security/contentSecurityPolicy/1.1/plugintypes-affects-cross-site-child-disallowed.html [ Failure ]
 
-# https://crbug.com/615156 - Need to make IntersectionObserver work with OOPIFs.
-crbug.com/615156 http/tests/intersection-observer/iframe-cross-origin.html [ Failure Timeout ]
-crbug.com/615156 virtual/mojo-loading/http/tests/intersection-observer/iframe-cross-origin.html [ Failure Timeout ]
-
 # https://crbug.com/616626 - allow_universal_access_from_file_urls doesn't work with --site-per-process.
 # https://crbug.com/665058 - EventSender drag-and-drop simulation doesn't support OOPIFs.
 crbug.com/665058 http/tests/local/drag-over-remote-content.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 005ce90..5a52109 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -357,8 +357,6 @@
 
 crbug.com/592183 usb/usbDevice.html [ Slow ]
 
-crbug.com/658318 [ Win Debug ] fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html [ Slow ]
-
 crbug.com/594189 virtual/spv2/fast/overflow/lots-of-sibling-inline-boxes.html [ Slow ]
 
 crbug.com/611442 imported/wpt/quirks-mode/hashless-hex-color.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour-expected.txt
deleted file mode 100644
index ff59ab2..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour-expected.txt
+++ /dev/null
@@ -1,172 +0,0 @@
-Check stepping-up and -down for time input fields from renderer. No cases of empty initial values.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Hours, 1-12
-PASS stepUp("07:00", 1, null, null) is "08:00"
-PASS stepDown("07:00", 1, null, null) is "06:00"
-PASS stepUp("11:00", 1, null, null) is "00:00"
-PASS stepDown("00:00", 1, null, null) is "11:00"
-PASS stepUp("06:00", 7200, null, null) is "08:00"
-PASS stepDown("06:00", 7200, null, null) is "04:00"
-PASS stepUp("10:00", 7200, null, null) is "00:00"
-PASS stepDown("00:00", 7200, null, null) is "10:00"
-PASS stepUp("07:00", 7200, null, null) is "08:00"
-PASS stepDown("07:00", 7200, null, null) is "06:00"
-PASS stepUp("06:00", 3601, null, null) is "07:00"
-PASS stepDown("06:00", 3601, null, null) is "05:00"
-PASS stepUp("06:00", 0, null, null) is "07:00"
-PASS stepDown("06:00", 0, null, null) is "05:00"
-PASS stepUp("06:00", 86400, null, null) is "00:00"
-PASS stepDown("06:00", 86400, null, null) is "00:00"
-PASS stepUp("06:00", 36000, null, null) is "07:00"
-PASS stepDown("06:00", 36000, null, null) is "05:00"
-PASS stepUp("06:00", 7200, "01:00", null) is "07:00"
-PASS stepDown("06:00", 7200, "01:00", null) is "05:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowUp"]) is "02:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowDown"]) is "00:00"
-PASS getUserAgentShadowTextContent(input) is "12:00 AM"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"]) is "01:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"]) is "11:00"
-PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
-PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
-PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
-PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
-PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepUp("12:00", 1, "12:00", "15:00") is "13:00"
-PASS stepDown("12:00", 1, "12:00", "15:00") is "23:00"
-PASS stepUp("15:00", 1, "12:00", "15:00") is "16:00"
-PASS stepDown("15:00", 1, "12:00", "15:00") is "14:00"
-PASS stepUp("12:00", 1, "10:00", "12:00") is "13:00"
-PASS stepDown("12:00", 1, "10:00", "12:00") is "23:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "11:00"
-PASS stepUp("15:00", 1, "10:00", "15:00") is "16:00"
-PASS stepDown("10:00", 1, "10:00", "15:00") is "09:00"
-PASS stepUp("17:00", 7200, "17:00", "20:00") is "19:00"
-PASS stepDown("17:00", 7200, "17:00", "20:00") is "19:00"
-PASS stepUp("17:00", 7200, "17:00", "18:00") is "17:00"
-PASS stepDown("17:00", 7200, "17:00", "18:00") is "17:00"
-Hours, 0-11
-PASS stepUp("11:00", 1, null, null) is "00:00"
-PASS getUserAgentShadowTextContent(input) is "00:00 AM"
-PASS stepDown("00:00", 1, null, null) is "11:00"
-PASS getUserAgentShadowTextContent(input) is "11:00 AM"
-PASS stepUp("23:00", 1, null, null) is "12:00"
-PASS getUserAgentShadowTextContent(input) is "00:00 PM"
-PASS stepDown("12:00", 1, null, null) is "23:00"
-PASS getUserAgentShadowTextContent(input) is "11:00 PM"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowUp"]) is "00:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowDown"]) is "10:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"]) is "01:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"]) is "11:00"
-PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
-PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
-PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
-PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
-PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepUp("12:00", 1, "12:00", "15:00") is "13:00"
-PASS stepDown("12:00", 1, "12:00", "15:00") is "15:00"
-PASS stepUp("15:00", 1, "12:00", "15:00") is "12:00"
-PASS stepDown("15:00", 1, "12:00", "15:00") is "14:00"
-PASS stepUp("12:00", 1, "10:00", "12:00") is "13:00"
-PASS stepDown("12:00", 1, "10:00", "12:00") is "23:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
-PASS stepUp("15:00", 1, "10:00", "15:00") is "16:00"
-PASS stepDown("10:00", 1, "10:00", "15:00") is "09:00"
-PASS stepUp("20:00", 7200, "17:00", "20:00") is "17:00"
-PASS stepDown("20:00", 7200, "17:00", "20:00") is "19:00"
-Hours, 0-23
-PASS stepUp("07:00", 1, null, null) is "08:00"
-PASS stepDown("07:00", 1, null, null) is "06:00"
-PASS stepUp("23:00", 1, null, null) is "00:00"
-PASS stepDown("00:00", 1, null, null) is "23:00"
-PASS stepUp("06:00", 7200, null, null) is "08:00"
-PASS stepDown("06:00", 7200, null, null) is "04:00"
-PASS stepUp("22:00", 7200, null, null) is "00:00"
-PASS stepDown("00:00", 7200, null, null) is "22:00"
-PASS stepUp("07:00", 7200, null, null) is "08:00"
-PASS stepDown("07:00", 7200, null, null) is "06:00"
-PASS stepUp("06:00", 3601, null, null) is "07:00"
-PASS stepDown("06:00", 3601, null, null) is "05:00"
-PASS stepUp("06:00", 0, null, null) is "07:00"
-PASS stepDown("06:00", 0, null, null) is "05:00"
-PASS stepUp("06:00", 86400, null, null) is "00:00"
-PASS stepDown("06:00", 86400, null, null) is "00:00"
-PASS stepUp("06:00", 36000, null, null) is "07:00"
-PASS stepDown("06:00", 36000, null, null) is "05:00"
-PASS stepUp("06:00", 7200, "01:00", null) is "07:00"
-PASS stepDown("06:00", 7200, "01:00", null) is "05:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowUp"]) is "00:00"
-PASS getUserAgentShadowTextContent(input) is "00:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowDown"]) is "22:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"]) is "01:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"]) is "23:00"
-PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
-PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
-PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
-PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
-PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
-PASS stepUp("03:00", 1, "00:00", "03:00") is "00:00"
-PASS stepDown("03:00", 1, "00:00", "03:00") is "02:00"
-PASS stepUp("12:00", 1, "10:00", "12:00") is "10:00"
-PASS stepDown("12:00", 1, "10:00", "12:00") is "11:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
-PASS stepUp("15:00", 1, "10:00", "15:00") is "10:00"
-PASS stepDown("10:00", 1, "10:00", "15:00") is "15:00"
-PASS stepUp("20:00", 7200, "17:00", "20:00") is "17:00"
-PASS stepDown("20:00", 7200, "17:00", "20:00") is "19:00"
-Hours, 1-24
-PASS stepUp("11:00", 1, null, null) is "12:00"
-PASS getUserAgentShadowTextContent(input) is "12:00"
-PASS stepDown("00:00", 1, null, null) is "23:00"
-PASS getUserAgentShadowTextContent(input) is "23:00"
-PASS stepUp("23:00", 1, null, null) is "00:00"
-PASS getUserAgentShadowTextContent(input) is "24:00"
-PASS stepDown("12:00", 1, null, null) is "11:00"
-PASS getUserAgentShadowTextContent(input) is "11:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowUp"]) is "02:00"
-PASS test("06:00", 7200, null, null, ["Delete", "ArrowDown"]) is "00:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"]) is "01:00"
-PASS test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"]) is "23:00"
-PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
-PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
-PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
-PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
-PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
-PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "23:00"
-PASS stepUp("03:00", 1, "00:00", "03:00") is "04:00"
-PASS stepDown("03:00", 1, "00:00", "03:00") is "02:00"
-PASS stepUp("12:00", 1, "10:00", "12:00") is "10:00"
-PASS stepDown("12:00", 1, "10:00", "12:00") is "11:00"
-PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
-PASS stepDown("00:00", 1, "00:00", "03:00") is "23:00"
-PASS stepUp("15:00", 1, "10:00", "15:00") is "10:00"
-PASS stepDown("10:00", 1, "10:00", "15:00") is "15:00"
-PASS stepUp("17:00", 7200, "17:00", "20:00") is "19:00"
-PASS stepDown("17:00", 7200, "17:00", "20:00") is "19:00"
-PASS stepUp("17:00", 7200, "17:00", "18:00") is "17:00"
-PASS stepDown("17:00", 7200, "17:00", "18:00") is "17:00"
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html b/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
index 84fd85a3..e37bae8 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-hour.html
@@ -1,237 +1,248 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
 <script src="../resources/common.js"></script>
 </head>
 <body>
 <script>
-description('Check stepping-up and -down for time input fields from renderer. No cases of empty initial values.');
-if (!window.internals)
-    testFailed('This test requires window.internals.');
-
+// We use a single INPUT element for multiple testcases to improve test performance.
 var input = document.createElement('input');
+input.type = 'time';
 document.body.appendChild(input);
-
-function keyDown(key, modifiers)
-{
-    if (!window.eventSender)
-        return;
-    eventSender.keyDown(key, modifiers);
-}
+input.focus();
 
 function setDateTimeFormat(pattern) {
-    getElementByPseudoId(internals.youngestShadowRoot(input), '-webkit-datetime-edit').setAttribute('pattern', pattern);
-    input.value = '';  // Updates the element for new format
+  getElementByPseudoId(internals.youngestShadowRoot(input), '-webkit-datetime-edit').setAttribute('pattern', pattern);
+  input.value = '';  // Updates the element for new format
 }
 
 function maybeUpdateAttribute(name, value) {
-    if (value) {
-        if (input.getAttribute(name) != value)
-            input.setAttribute(name, value);
-    } else {
-        if (input.hasAttribute(name))
-            input.removeAttribute(name);
-    }
+  if (value) {
+    if (input.getAttribute(name) !== value)
+      input.setAttribute(name, value);
+  } else {
+    if (input.hasAttribute(name))
+      input.removeAttribute(name);
+  }
 }
 
-function setInputAttributes(value, min, max, step) {
-    input.value = value;
-    maybeUpdateAttribute('min', min);
-    maybeUpdateAttribute('max', max);
-    maybeUpdateAttribute('step', step);
+function assert_input_value_with_limits(initial, step, min, max, keySequence, expected, expectedDisplayValue) {
+  input.value = initial;
+  maybeUpdateAttribute('min', min);
+  maybeUpdateAttribute('max', max);
+  maybeUpdateAttribute('step', step);
+
+  keySequence.forEach(key => { eventSender.keyDown(key); });
+
+  assert_equals(input.value, expected);
+  if (expectedDisplayValue)
+    assert_equals(getUserAgentShadowTextContent(input), expectedDisplayValue);
 }
 
-function test(value, step, min, max, keySequence) {
-    setInputAttributes(value, min, max, step);
-    for (var i = 0; i < keySequence.length; i++)
-        keyDown(keySequence[i]);
-    return input.value;
+function assert_input_value_with_limits_after_up(initial, step, min, max, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, min, max, ['ArrowUp'], expected, expectedDisplayValue);
 }
 
-function stepUp(value, step, min, max) {
-    return test(value, step, min, max, ['ArrowUp']);
+function assert_input_value_with_limits_after_down(initial, step, min, max, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, min, max, ['ArrowDown'], expected, expectedDisplayValue);
 }
 
-function stepDown(value, step, min, max) {
-    return test(value, step, min, max, ['ArrowDown']);
+function assert_input_value_with_limits_after_deleteup(initial, step, min, max, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, min, max, ['Delete', 'ArrowUp'], expected, expectedDisplayValue);
 }
 
-input.type = 'time';
-input.focus();
+function assert_input_value_with_limits_after_deletedown(initial, step, min, max, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, min, max, ['Delete', 'ArrowDown'], expected, expectedDisplayValue);
+}
 
-debug('Hours, 1-12');
-shouldBeEqualToString('stepUp("07:00", 1, null, null)', '08:00');
-shouldBeEqualToString('stepDown("07:00", 1, null, null)', '06:00');
-shouldBeEqualToString('stepUp("11:00", 1, null, null)', '00:00');
-shouldBeEqualToString('stepDown("00:00", 1, null, null)', '11:00');
-shouldBeEqualToString('stepUp("06:00", 7200, null, null)', '08:00');
-shouldBeEqualToString('stepDown("06:00", 7200, null, null)', '04:00');
-shouldBeEqualToString('stepUp("10:00", 7200, null, null)', '00:00');
-shouldBeEqualToString('stepDown("00:00", 7200, null, null)', '10:00');
-shouldBeEqualToString('stepUp("07:00", 7200, null, null)', '08:00');
-shouldBeEqualToString('stepDown("07:00", 7200, null, null)', '06:00');
-shouldBeEqualToString('stepUp("06:00", 3601, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 3601, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 0, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 0, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 86400, null, null)', '00:00');
-shouldBeEqualToString('stepDown("06:00", 86400, null, null)', '00:00');
-shouldBeEqualToString('stepUp("06:00", 36000, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 36000, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 7200, "01:00", null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 7200, "01:00", null)', '05:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowUp"])', '02:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowDown"])', '00:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '12:00 AM');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"])', '01:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"])', '11:00');
-shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
-shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
-shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
-shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepUp("12:00", 1, "12:00", "15:00")', '13:00');
-shouldBeEqualToString('stepDown("12:00", 1, "12:00", "15:00")', '23:00');
-shouldBeEqualToString('stepUp("15:00", 1, "12:00", "15:00")', '16:00');
-shouldBeEqualToString('stepDown("15:00", 1, "12:00", "15:00")', '14:00');
-shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '13:00');
-shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '23:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '11:00');
-shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '16:00');
-shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '09:00');
-shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "20:00")', '19:00');
-shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "20:00")', '19:00');
-shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "18:00")', '17:00');
-shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "18:00")', '17:00');
+function assert_input_value_after_up(initial, step, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, null, null, ['ArrowUp'], expected, expectedDisplayValue);
+}
 
-debug('Hours, 0-11');
-setDateTimeFormat('KK:mm a');
-shouldBeEqualToString('stepUp("11:00", 1, null, null)', '00:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '00:00 AM');
-shouldBeEqualToString('stepDown("00:00", 1, null, null)', '11:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '11:00 AM');
-shouldBeEqualToString('stepUp("23:00", 1, null, null)', '12:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '00:00 PM');
-shouldBeEqualToString('stepDown("12:00", 1, null, null)', '23:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '11:00 PM');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowUp"])', '00:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowDown"])', '10:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"])', '01:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"])', '11:00');
-shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
-shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
-shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
-shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepUp("12:00", 1, "12:00", "15:00")', '13:00');
-shouldBeEqualToString('stepDown("12:00", 1, "12:00", "15:00")', '15:00');
-shouldBeEqualToString('stepUp("15:00", 1, "12:00", "15:00")', '12:00');
-shouldBeEqualToString('stepDown("15:00", 1, "12:00", "15:00")', '14:00');
-shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '13:00');
-shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '23:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
-shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '16:00');
-shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '09:00');
-shouldBeEqualToString('stepUp("20:00", 7200, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("20:00", 7200, "17:00", "20:00")', '19:00');
+function assert_input_value_after_down(initial, step, expected, expectedDisplayValue) {
+  assert_input_value_with_limits(initial, step, null, null, ['ArrowDown'], expected, expectedDisplayValue);
+}
 
-debug('Hours, 0-23');
-setDateTimeFormat('HH:mm');
-shouldBeEqualToString('stepUp("07:00", 1, null, null)', '08:00');
-shouldBeEqualToString('stepDown("07:00", 1, null, null)', '06:00');
-shouldBeEqualToString('stepUp("23:00", 1, null, null)', '00:00');
-shouldBeEqualToString('stepDown("00:00", 1, null, null)', '23:00');
-shouldBeEqualToString('stepUp("06:00", 7200, null, null)', '08:00');
-shouldBeEqualToString('stepDown("06:00", 7200, null, null)', '04:00');
-shouldBeEqualToString('stepUp("22:00", 7200, null, null)', '00:00');
-shouldBeEqualToString('stepDown("00:00", 7200, null, null)', '22:00');
-shouldBeEqualToString('stepUp("07:00", 7200, null, null)', '08:00');
-shouldBeEqualToString('stepDown("07:00", 7200, null, null)', '06:00');
-shouldBeEqualToString('stepUp("06:00", 3601, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 3601, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 0, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 0, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 86400, null, null)', '00:00');
-shouldBeEqualToString('stepDown("06:00", 86400, null, null)', '00:00');
-shouldBeEqualToString('stepUp("06:00", 36000, null, null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 36000, null, null)', '05:00');
-shouldBeEqualToString('stepUp("06:00", 7200, "01:00", null)', '07:00');
-shouldBeEqualToString('stepDown("06:00", 7200, "01:00", null)', '05:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowUp"])', '00:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '00:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowDown"])', '22:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"])', '01:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"])', '23:00');
-shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
-shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
-shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
-shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
-shouldBeEqualToString('stepUp("03:00", 1, "00:00", "03:00")', '00:00');
-shouldBeEqualToString('stepDown("03:00", 1, "00:00", "03:00")', '02:00');
-shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '10:00');
-shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '11:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
-shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '10:00');
-shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '15:00');
-shouldBeEqualToString('stepUp("20:00", 7200, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("20:00", 7200, "17:00", "20:00")', '19:00');
+// Hours, 1-12
+test(() => {
+  assert_exists(window, 'internals');
+  assert_exists(window, 'eventSender');
 
-debug('Hours, 1-24');
-setDateTimeFormat('kk:mm');
-shouldBeEqualToString('stepUp("11:00", 1, null, null)', '12:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '12:00');
-shouldBeEqualToString('stepDown("00:00", 1, null, null)', '23:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '23:00');
-shouldBeEqualToString('stepUp("23:00", 1, null, null)', '00:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '24:00');
-shouldBeEqualToString('stepDown("12:00", 1, null, null)', '11:00');
-shouldBeEqualToString('getUserAgentShadowTextContent(input)', '11:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowUp"])', '02:00');
-shouldBeEqualToString('test("06:00", 7200, null, null, ["Delete", "ArrowDown"])', '00:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowUp"])', '01:00');
-shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["Delete", "ArrowDown"])', '23:00');
-shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
-shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
-shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
-shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
-shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
-shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '23:00');
-shouldBeEqualToString('stepUp("03:00", 1, "00:00", "03:00")', '04:00');
-shouldBeEqualToString('stepDown("03:00", 1, "00:00", "03:00")', '02:00');
-shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '10:00');
-shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '11:00');
-shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
-shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '23:00');
-shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '10:00');
-shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '15:00');
-shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "20:00")', '19:00');
-shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "20:00")', '19:00');
-shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "18:00")', '17:00');
-shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "18:00")', '17:00');
+  assert_input_value_after_up('07:00', 1, '08:00');
+  assert_input_value_after_down('07:00', 1, '06:00');
+  assert_input_value_after_up('11:00', 1, '00:00');
+  assert_input_value_after_down('00:00', 1, '11:00');
+  assert_input_value_after_up('06:00', 7200, '08:00');
+  assert_input_value_after_down('06:00', 7200, '04:00');
+  assert_input_value_after_up('10:00', 7200, '00:00');
+  assert_input_value_after_down('00:00', 7200, '10:00');
+  assert_input_value_after_up('07:00', 7200, '08:00');
+  assert_input_value_after_down('07:00', 7200, '06:00');
+  assert_input_value_after_up('06:00', 3601, '07:00');
+  assert_input_value_after_down('06:00', 3601, '05:00');
+  assert_input_value_after_up('06:00', 0, '07:00');
+  assert_input_value_after_down('06:00', 0, '05:00');
+  assert_input_value_after_up('06:00', 86400, '00:00');
+  assert_input_value_after_down('06:00', 86400, '00:00');
+  assert_input_value_after_up('06:00', 36000, '07:00');
+  assert_input_value_after_down('06:00', 36000, '05:00');
+  assert_input_value_with_limits_after_up('06:00', 7200, '01:00', null, '07:00');
+  assert_input_value_with_limits_after_down('06:00', 7200, '01:00', null, '05:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, null, null, '02:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, null, null, '00:00', '12:00 AM');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, '01:00', null, '01:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, '01:00', null, '11:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '17:00', '20:00', '18:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '15:00', '17:00', '15:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '15:00', '17:00', '16:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '12:00', '15:00', '13:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '12:00', '15:00', '23:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '12:00', '15:00', '16:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '12:00', '15:00', '14:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '10:00', '12:00', '13:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '23:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '11:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '10:00', '15:00', '16:00');
+  assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '09:00');
+  assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '20:00', '19:00');
+  assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '20:00', '19:00');
+  assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '18:00', '17:00');
+  assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '18:00', '17:00');
+}, 'Hours, 1-12');
 
-setDateTimeFormat('');
-debug('');
-document.body.removeChild(input);
+// Hours, 0-11
+test(() => {
+  assert_exists(window, 'internals');
+  assert_exists(window, 'eventSender');
+  setDateTimeFormat('KK:mm a');
+
+  assert_input_value_after_up('11:00', 1, '00:00', '00:00 AM');
+  assert_input_value_after_down('00:00', 1, '11:00', '11:00 AM');
+  assert_input_value_after_up('23:00', 1, '12:00', '00:00 PM');
+  assert_input_value_after_down('12:00', 1, '23:00', '11:00 PM');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, null, null, '00:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, null, null, '10:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, '01:00', null, '01:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, '01:00', null, '11:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '17:00', '20:00', '18:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '15:00', '17:00', '15:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '15:00', '17:00', '16:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '12:00', '15:00', '13:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '12:00', '15:00', '15:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '12:00', '15:00', '12:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '12:00', '15:00', '14:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '10:00', '12:00', '13:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '23:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '03:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '10:00', '15:00', '16:00');
+  assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '09:00');
+  assert_input_value_with_limits_after_up('20:00', 7200, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('20:00', 7200, '17:00', '20:00', '19:00');
+}, 'Hours, 0-11');
+
+// Hours, 0-23
+test(() => {
+  assert_exists(window, 'internals');
+  assert_exists(window, 'eventSender');
+  setDateTimeFormat('HH:mm');
+
+  assert_input_value_after_up('07:00', 1, '08:00');
+  assert_input_value_after_down('07:00', 1, '06:00');
+  assert_input_value_after_up('23:00', 1, '00:00');
+  assert_input_value_after_down('00:00', 1, '23:00');
+  assert_input_value_after_up('06:00', 7200, '08:00');
+  assert_input_value_after_down('06:00', 7200, '04:00');
+  assert_input_value_after_up('22:00', 7200, '00:00');
+  assert_input_value_after_down('00:00', 7200, '22:00');
+  assert_input_value_after_up('07:00', 7200, '08:00');
+  assert_input_value_after_down('07:00', 7200, '06:00');
+  assert_input_value_after_up('06:00', 3601, '07:00');
+  assert_input_value_after_down('06:00', 3601, '05:00');
+  assert_input_value_after_up('06:00', 0, '07:00');
+  assert_input_value_after_down('06:00', 0, '05:00');
+  assert_input_value_after_up('06:00', 86400, '00:00');
+  assert_input_value_after_down('06:00', 86400, '00:00');
+  assert_input_value_after_up('06:00', 36000, '07:00');
+  assert_input_value_after_down('06:00', 36000, '05:00');
+  assert_input_value_with_limits_after_up('06:00', 7200, '01:00', null, '07:00');
+  assert_input_value_with_limits_after_down('06:00', 7200, '01:00', null, '05:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, null, null, '00:00', '00:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, null, null, '22:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, '01:00', null, '01:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, '01:00', null, '23:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '17:00', '20:00', '18:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '15:00', '17:00', '15:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '15:00', '17:00', '16:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '03:00');
+  assert_input_value_with_limits_after_up('03:00', 1, '00:00', '03:00', '00:00');
+  assert_input_value_with_limits_after_down('03:00', 1, '00:00', '03:00', '02:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '10:00', '12:00', '10:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '11:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '03:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '10:00', '15:00', '10:00');
+  assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '15:00');
+  assert_input_value_with_limits_after_up('20:00', 7200, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('20:00', 7200, '17:00', '20:00', '19:00');
+}, 'Hours, 0-23');
+
+// Hours, 1-24
+test(() => {
+  assert_exists(window, 'internals');
+  assert_exists(window, 'eventSender');
+  setDateTimeFormat('kk:mm');
+
+  assert_input_value_after_up('11:00', 1, '12:00', '12:00');
+  assert_input_value_after_down('00:00', 1, '23:00', '23:00');
+  assert_input_value_after_up('23:00', 1, '00:00', '24:00');
+  assert_input_value_after_down('12:00', 1, '11:00', '11:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, null, null, '02:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, null, null, '00:00');
+  assert_input_value_with_limits_after_deleteup('06:00', 7200, '01:00', null, '01:00');
+  assert_input_value_with_limits_after_deletedown('06:00', 7200, '01:00', null, '23:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '17:00', '20:00', '18:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('17:00', 1, '15:00', '17:00', '15:00');
+  assert_input_value_with_limits_after_down('17:00', 1, '15:00', '17:00', '16:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '17:00', '20:00', '17:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '17:00', '20:00', '20:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_down('15:00', 1, '13:00', '13:00', '13:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '23:00');
+  assert_input_value_with_limits_after_up('03:00', 1, '00:00', '03:00', '04:00');
+  assert_input_value_with_limits_after_down('03:00', 1, '00:00', '03:00', '02:00');
+  assert_input_value_with_limits_after_up('12:00', 1, '10:00', '12:00', '10:00');
+  assert_input_value_with_limits_after_down('12:00', 1, '10:00', '12:00', '11:00');
+  assert_input_value_with_limits_after_up('00:00', 1, '00:00', '03:00', '01:00');
+  assert_input_value_with_limits_after_down('00:00', 1, '00:00', '03:00', '23:00');
+  assert_input_value_with_limits_after_up('15:00', 1, '10:00', '15:00', '10:00');
+  assert_input_value_with_limits_after_down('10:00', 1, '10:00', '15:00', '15:00');
+  assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '20:00', '19:00');
+  assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '20:00', '19:00');
+  assert_input_value_with_limits_after_up('17:00', 7200, '17:00', '18:00', '17:00');
+  assert_input_value_with_limits_after_down('17:00', 7200, '17:00', '18:00', '17:00');
+}, 'Hours, 1-24');
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/fast/parser/inselect-tokenization.html b/third_party/WebKit/LayoutTests/fast/parser/inselect-tokenization.html
new file mode 100644
index 0000000..997cfbd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/parser/inselect-tokenization.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<body>
+<script>
+  /************************************************************************
+   *   Helper functions!
+   */
+  function createFrame(markup) {
+    var i = document.createElement('iframe');
+    i.srcdoc = markup;
+    return i;
+  }
+
+  function appendAndWaitForLoad(test, frame) {
+    return new Promise((resolve, reject) => {
+      frame.onload = test.step_func(_ => {
+        frame.onload = null;
+        resolve();
+      });
+      document.body.appendChild(frame);
+    });
+  }
+
+  function assert_select(test, frame, value) {
+    var select = frame.contentDocument.querySelector('select');
+    assert_equals(select.value, value, 'select');
+  }
+
+  function assert_element_innerText(test, frame, name, value) {
+    var el = frame.contentDocument.querySelector(name);
+    if (value === null || value === undefined)
+      assert_equals(el, null, name);
+    else
+      assert_equals(el.innerText, value, name);
+  }
+
+  /************************************************************************
+   *   The actual tests!
+   */
+  var tests = [
+    // <input>, <keygen>, and <textarea> close <select>, so <plaintext> works.
+  ];
+
+  var elementsToIgnore = [
+    "iframe",
+    "noembed",
+    "noframes",
+    "noscript",
+    "plaintext",
+    "style",
+    "xmp",
+  ];
+
+  elementsToIgnore.forEach(el => {
+    tests.push(
+      {
+        markup: `<form><select><option><${el}>1<element></element>`,
+        select: "1",
+        innerText: null,
+        name: el
+      }, {
+        markup: `<form><select><option>1<${el}>2<element></element>`,
+        select: "12",
+        innerText: null,
+        name: el
+      }, {
+        markup: `<form><select><option>1<${el}>2<element></element>3`,
+        select: "123",
+        innerText: null,
+        name: el
+      });
+    if (el != "iframe") {
+      tests.push(
+        {
+          markup: `<form><select><option>1<input><${el}>2<element></element>`,
+          select: "1",
+          innerText: "2<element></element>",
+          name: el
+        }, {
+          markup: `<form><select><option>1<keygen><${el}>2<element></element>`,
+          select: "1",
+          innerText: "2<element></element>",
+          name: el
+        }, {
+          markup: `<form><select><option>1<textarea></textarea><${el}>2<element></element>`,
+          select: "1",
+          innerText: "2<element></element>",
+          name: el
+        });
+    }
+  });
+  
+
+  tests.forEach(test => {
+    async_test(t => {
+      var i = createFrame(test.markup);
+
+      appendAndWaitForLoad(t, i)
+        .then(t.step_func_done(_ => {
+          assert_select(t, i, test.select);
+          assert_element_innerText(t, i, test.name, test.innerText);
+        }));
+    }, test.markup);
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/option.html b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/option.html
new file mode 100644
index 0000000..925e8a5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/option.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/helper.js"></script>
+<body>
+<script>
+
+  var tests = [
+    `
+      <form action="/security/resources/postmessage-post.php" method="post">
+        <input type="submit">
+        <select name="dangling"><option>
+    `,
+    `
+      <div>
+        <form action="/security/resources/postmessage-post.php" method="post">
+          <input type="submit">
+          <select name="dangling"><option>
+    `,
+    `
+      <form action="/security/resources/postmessage-post.php" method="post" id="form">
+        <input type="submit">
+      </form>
+      <select name="dangling" form="form"><option>
+    `,
+    `
+      <form action="/security/resources/postmessage-post.php" method="post">
+        <input type="submit">
+        <select name="dangling"><option label="yay">
+    `,
+    `
+      <div>
+        <form action="/security/resources/postmessage-post.php" method="post">
+          <input type="submit">
+          <select name="dangling"><option label="yay">
+    `,
+    `
+      <form action="/security/resources/postmessage-post.php" method="post" id="form">
+        <input type="submit">
+      </form>
+      <select name="dangling" form="form"><option label="yay">
+    `
+  ];
+
+  tests.forEach(markup => {
+    async_test(t => {
+      var i = createFrame(`${markup}sekrit<element attribute></element>`);
+      assert_no_submission(t, i);
+    }, markup.replace(/[\n\r]/g, ''));
+  });
+</script>
+  
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js
new file mode 100644
index 0000000..7d5ad6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js
@@ -0,0 +1,37 @@
+function assert_no_message_from_frame(test, frame) {
+  window.addEventListener("message", test.step_func(e => {
+    assert_not_equals(e.source, frame.contentWindow);
+  }));
+}
+
+function appendAndSubmit(test, frame) {
+  return new Promise((resolve, reject) => {
+    frame.onload = test.step_func(_ => {
+      frame.onload = null;
+      frame.contentDocument.querySelector('form').addEventListener("error", _ => {
+        resolve("error");
+      });
+      frame.contentDocument.querySelector('form').addEventListener("submit", _ => {
+        resolve("submit");
+      });
+      frame.contentDocument.querySelector('[type=submit]').click();
+    });
+    document.body.appendChild(frame);
+  });
+}
+
+function assert_no_submission(test, frame) {
+  assert_no_message_from_frame(test, frame);
+
+  appendAndSubmit(test, frame)
+    .then(test.step_func_done(result => {
+      assert_equals(result, "error");
+      frame.remove();
+    }));
+}
+
+function createFrame(markup) {
+  var i = document.createElement('iframe');
+  i.srcdoc = `${markup}sekrit`;
+  return i;
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/textarea.html b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/textarea.html
new file mode 100644
index 0000000..27558821
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/textarea.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/helper.js"></script>
+<body>
+<script>
+
+  var tests = [
+    `
+      <form action="/security/resources/postmessage-post.php" method="post">
+        <input type="submit">
+        <textarea name="dangling">
+    `,
+    `
+      <div>
+        <form action="/security/resources/postmessage-post.php" method="post">
+          <input type="submit">
+          <textarea name="dangling">
+    `,
+    `
+      <form action="/security/resources/postmessage-post.php" method="post" id="form">
+        <input type="submit">
+      </form>
+      <textarea name="dangling" form="form">
+    `
+  ];
+
+  tests.forEach(markup => {
+    async_test(t => {
+      var i = createFrame(`${markup}sekrit<element attribute></element>`);
+      assert_no_submission(t, i);
+    }, markup.replace(/[\n\r]/g, ''));
+  });
+</script>
+  
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/resources/postmessage-post.php b/third_party/WebKit/LayoutTests/http/tests/security/resources/postmessage-post.php
new file mode 100644
index 0000000..5f2c3f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/resources/postmessage-post.php
@@ -0,0 +1,5 @@
+<script>
+top.postMessage(<?php
+    echo json_encode($_POST);
+?>, "*");
+</script>
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp b/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
index a5d8814..36bdec18 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
@@ -195,6 +195,15 @@
     case v8::Isolate::kFunctionConstructorReturnedUndefined:
       blinkFeature = UseCounter::V8FunctionConstructorReturnedUndefined;
       break;
+    case v8::Isolate::kAssigmentExpressionLHSIsCallInSloppy:
+      blinkFeature = UseCounter::V8AssigmentExpressionLHSIsCallInSloppy;
+      break;
+    case v8::Isolate::kAssigmentExpressionLHSIsCallInStrict:
+      blinkFeature = UseCounter::V8AssigmentExpressionLHSIsCallInStrict;
+      break;
+    case v8::Isolate::kPromiseConstructorReturnedUndefined:
+      blinkFeature = UseCounter::V8PromiseConstructorReturnedUndefined;
+      break;
     default:
       // This can happen if V8 has added counters that this version of Blink
       // does not know about. It's harmless.
diff --git a/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp b/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
index e7e1182..2570be9c 100644
--- a/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
+++ b/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
@@ -76,13 +76,13 @@
     m_callbackSelectorChangeTimer.startOneShot(0, BLINK_FROM_HERE);
     return;
   }
-  if (host()->frame()) {
+  if (supplementable()->frame()) {
     Vector<String> addedSelectors;
     Vector<String> removedSelectors;
     copyToVector(m_addedSelectors, addedSelectors);
     copyToVector(m_removedSelectors, removedSelectors);
-    host()->frame()->loader().client()->selectorMatchChanged(addedSelectors,
-                                                             removedSelectors);
+    supplementable()->frame()->loader().client()->selectorMatchChanged(
+        addedSelectors, removedSelectors);
   }
   m_addedSelectors.clear();
   m_removedSelectors.clear();
@@ -164,7 +164,7 @@
     m_watchedCallbackSelectors.push_back(
         StyleRule::create(std::move(selectorList), callbackPropertySet));
   }
-  host()->styleEngine().watchedSelectorsChanged();
+  supplementable()->styleEngine().watchedSelectorsChanged();
 }
 
 DEFINE_TRACE(CSSSelectorWatch) {
diff --git a/third_party/WebKit/Source/core/dom/DocumentParserTiming.cpp b/third_party/WebKit/Source/core/dom/DocumentParserTiming.cpp
index 3abaf1db..850185e 100644
--- a/third_party/WebKit/Source/core/dom/DocumentParserTiming.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentParserTiming.cpp
@@ -72,8 +72,8 @@
     : Supplement<Document>(document) {}
 
 void DocumentParserTiming::notifyDocumentParserTimingChanged() {
-  if (host()->loader())
-    host()->loader()->didChangePerformanceTiming();
+  if (supplementable()->loader())
+    supplementable()->loader()->didChangePerformanceTiming();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 19b5a0a..dff9ec6c 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -52,6 +52,8 @@
 #include "core/frame/Location.h"
 #include "core/frame/PageScaleConstraintsSet.h"
 #include "core/frame/PerformanceMonitor.h"
+#include "core/frame/RemoteFrame.h"
+#include "core/frame/RemoteFrameView.h"
 #include "core/frame/Settings.h"
 #include "core/frame/VisualViewport.h"
 #include "core/html/HTMLFrameElement.h"
@@ -109,6 +111,7 @@
 #include "platform/geometry/DoubleRect.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/geometry/LayoutRect.h"
+#include "platform/geometry/TransformState.h"
 #include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/graphics/GraphicsLayerDebugInfo.h"
@@ -4931,4 +4934,62 @@
   return reasons;
 }
 
+void FrameView::setViewportIntersectionFromParent(
+    const IntRect& viewportIntersection) {
+  if (m_remoteViewportIntersection != viewportIntersection) {
+    m_remoteViewportIntersection = viewportIntersection;
+    scheduleAnimation();
+  }
+}
+
+IntRect FrameView::remoteViewportIntersection() {
+  IntRect intersection(m_remoteViewportIntersection);
+  intersection.move(scrollOffsetInt());
+  return intersection;
+}
+
+void FrameView::mapQuadToAncestorFrameIncludingScrollOffset(
+    LayoutRect& rect,
+    const LayoutObject* descendant,
+    const LayoutView* ancestor,
+    MapCoordinatesFlags mode) {
+  FloatQuad mappedQuad = descendant->localToAncestorQuad(
+      FloatQuad(FloatRect(rect)), ancestor, mode);
+  rect = LayoutRect(mappedQuad.boundingBox());
+
+  // localToAncestorQuad accounts for scroll offset if it encounters a remote
+  // frame in the ancestor chain, otherwise it needs to be added explicitly.
+  if (frame().localFrameRoot() == frame().tree().top() ||
+      (ancestor &&
+       ancestor->frame()->localFrameRoot() == frame().localFrameRoot())) {
+    FrameView* ancestorView =
+        (ancestor ? ancestor->frameView()
+                  : toLocalFrame(frame().tree().top())->view());
+    LayoutSize scrollPosition = LayoutSize(ancestorView->getScrollOffset());
+    rect.move(-scrollPosition);
+  }
+}
+
+bool FrameView::mapToVisualRectInTopFrameSpace(LayoutRect& rect) {
+  // This is the top-level frame, so no mapping necessary.
+  if (m_frame->isMainFrame())
+    return true;
+
+  LayoutRect viewportIntersectionRect(remoteViewportIntersection());
+  rect.intersect(viewportIntersectionRect);
+  if (rect.isEmpty())
+    return false;
+  return true;
+}
+
+void FrameView::applyTransformForTopFrameSpace(TransformState& transformState) {
+  // This is the top-level frame, so no mapping necessary.
+  if (m_frame->isMainFrame())
+    return;
+
+  LayoutRect viewportIntersectionRect(remoteViewportIntersection());
+  transformState.move(
+      LayoutSize(-viewportIntersectionRect.x(), -viewportIntersectionRect.y()));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h
index fb210b8..983b3282 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.h
+++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -31,6 +31,7 @@
 #include "core/frame/FrameViewAutoSizeInfo.h"
 #include "core/frame/LayoutSubtreeRootList.h"
 #include "core/frame/RootFrameViewport.h"
+#include "core/layout/MapCoordinatesFlags.h"
 #include "core/layout/ScrollAnchor.h"
 #include "core/paint/FirstMeaningfulPaintDetector.h"
 #include "core/paint/ObjectPaintProperties.h"
@@ -87,6 +88,7 @@
 class Page;
 class ScrollingCoordinator;
 class TracedValue;
+class TransformState;
 struct AnnotatedRegionValue;
 struct CompositedSelection;
 
@@ -795,6 +797,28 @@
 
   bool hasVisibleSlowRepaintViewportConstrainedObjects() const;
 
+  // Called on a view for a LocalFrame with a RemoteFrame parent. This makes
+  // viewport intersection available that accounts for remote ancestor frames
+  // and their respective scroll positions, clips, etc.
+  void setViewportIntersectionFromParent(const IntRect&);
+  IntRect remoteViewportIntersection();
+
+  // This method uses localToAncestorQuad to map a rect into an ancestor's
+  // coordinate space, while guaranteeing that the top-level scroll offset
+  // is accounted for. This is needed because LayoutView::mapLocalToAncestor()
+  // implicitly includes the ancestor frame's scroll offset when there is
+  // a remote frame in the ancestor chain, but does not include it when
+  // there are only local frames in the frame tree.
+  void mapQuadToAncestorFrameIncludingScrollOffset(
+      LayoutRect&,
+      const LayoutObject* descendant,
+      const LayoutView* ancestor,
+      MapCoordinatesFlags mode);
+
+  bool mapToVisualRectInTopFrameSpace(LayoutRect&);
+
+  void applyTransformForTopFrameSpace(TransformState&);
+
  protected:
   // Scroll the content via the compositor.
   bool scrollContentsFastPath(const IntSize& scrollDelta);
@@ -1145,6 +1169,8 @@
 
   Member<ElementVisibilityObserver> m_visibilityObserver;
 
+  IntRect m_remoteViewportIntersection;
+
   // For testing.
   struct ObjectPaintInvalidation {
     String name;
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
index 652f833..1c5816e4f 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
@@ -137,15 +137,6 @@
   client()->forwardInputEvent(event);
 }
 
-void RemoteFrame::frameRectsChanged(const IntRect& frameRect) {
-  client()->frameRectsChanged(frameRect);
-}
-
-void RemoteFrame::visibilityChanged(bool visible) {
-  if (client())
-    client()->visibilityChanged(visible);
-}
-
 void RemoteFrame::setView(RemoteFrameView* view) {
   // Oilpan: as RemoteFrameView performs no finalization actions,
   // no explicit dispose() of it needed here. (cf. FrameView::dispose().)
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.h b/third_party/WebKit/Source/core/frame/RemoteFrame.h
index 412ec72e8..bf3a09a 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrame.h
+++ b/third_party/WebKit/Source/core/frame/RemoteFrame.h
@@ -13,7 +13,6 @@
 namespace blink {
 
 class Event;
-class IntRect;
 class LocalFrame;
 class RemoteFrameClient;
 class RemoteFrameView;
@@ -48,10 +47,6 @@
   // process. See http://crbug.com/339659.
   void forwardInputEvent(Event*);
 
-  void frameRectsChanged(const IntRect& frameRect);
-
-  void visibilityChanged(bool visible);
-
   void setWebLayer(WebLayer*);
   WebLayer* webLayer() const { return m_webLayer; }
 
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameClient.h b/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
index d1badcc..d1cec960 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
@@ -39,6 +39,9 @@
 
   virtual void frameRectsChanged(const IntRect& frameRect) = 0;
 
+  virtual void updateRemoteViewportIntersection(
+      const IntRect& viewportIntersection) = 0;
+
   virtual void advanceFocus(WebFocusType, LocalFrame* source) = 0;
 
   virtual void visibilityChanged(bool visible) = 0;
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameView.cpp b/third_party/WebKit/Source/core/frame/RemoteFrameView.cpp
index d73ac0a..fb2ad9df 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/RemoteFrameView.cpp
@@ -4,9 +4,13 @@
 
 #include "core/frame/RemoteFrameView.h"
 
+#include "core/dom/IntersectionObserverEntry.h"
 #include "core/frame/FrameView.h"
+#include "core/frame/LocalFrame.h"
 #include "core/frame/RemoteFrame.h"
+#include "core/frame/RemoteFrameClient.h"
 #include "core/html/HTMLFrameOwnerElement.h"
+#include "core/layout/LayoutView.h"
 #include "core/layout/api/LayoutPartItem.h"
 
 namespace blink {
@@ -29,6 +33,41 @@
   return view;
 }
 
+void RemoteFrameView::updateRemoteViewportIntersection() {
+  if (!m_remoteFrame->ownerLayoutObject())
+    return;
+
+  FrameView* localRootView =
+      toLocalFrame(m_remoteFrame->tree().parent())->localFrameRoot()->view();
+  if (!localRootView)
+    return;
+
+  // Start with rect in remote frame's coordinate space. Then
+  // mapToVisualRectInAncestorSpace will move it to the local root's coordinate
+  // space and account for any clip from containing elements such as a
+  // scrollable div. Passing nullptr as an argument to
+  // mapToVisualRectInAncestorSpace causes it to be clipped to the viewport,
+  // even if there are RemoteFrame ancestors in the frame tree.
+  LayoutRect rect(0, 0, frameRect().width(), frameRect().height());
+  rect.move(m_remoteFrame->ownerLayoutObject()->contentBoxOffset());
+  if (!m_remoteFrame->ownerLayoutObject()->mapToVisualRectInAncestorSpace(
+          nullptr, rect))
+    return;
+  IntRect rootVisibleRect = localRootView->visibleContentRect();
+  IntRect viewportIntersection(rect);
+  viewportIntersection.intersect(rootVisibleRect);
+  viewportIntersection.move(-localRootView->scrollOffsetInt());
+
+  // Translate the intersection rect from the root frame's coordinate space
+  // to the remote frame's coordinate space.
+  viewportIntersection = convertFromRootFrame(viewportIntersection);
+  if (viewportIntersection != m_lastViewportIntersection) {
+    m_remoteFrame->client()->updateRemoteViewportIntersection(
+        viewportIntersection);
+  }
+  m_lastViewportIntersection = viewportIntersection;
+}
+
 void RemoteFrameView::dispose() {
   HTMLFrameOwnerElement* ownerElement = m_remoteFrame->deprecatedLocalOwner();
   // ownerElement can be null during frame swaps, because the
@@ -68,7 +107,9 @@
   if (parent() && parent()->isFrameView())
     newRect = parent()->convertToRootFrame(
         toFrameView(parent())->contentsToFrame(newRect));
-  m_remoteFrame->frameRectsChanged(newRect);
+  m_remoteFrame->client()->frameRectsChanged(newRect);
+
+  updateRemoteViewportIntersection();
 }
 
 void RemoteFrameView::hide() {
@@ -76,7 +117,7 @@
 
   Widget::hide();
 
-  m_remoteFrame->visibilityChanged(false);
+  m_remoteFrame->client()->visibilityChanged(false);
 }
 
 void RemoteFrameView::show() {
@@ -84,7 +125,7 @@
 
   Widget::show();
 
-  m_remoteFrame->visibilityChanged(true);
+  m_remoteFrame->client()->visibilityChanged(true);
 }
 
 void RemoteFrameView::setParentVisible(bool visible) {
@@ -95,7 +136,7 @@
   if (!isSelfVisible())
     return;
 
-  m_remoteFrame->visibilityChanged(isVisible());
+  m_remoteFrame->client()->visibilityChanged(isVisible());
 }
 
 DEFINE_TRACE(RemoteFrameView) {
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameView.h b/third_party/WebKit/Source/core/frame/RemoteFrameView.h
index 6f243f4..c117abb 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrameView.h
+++ b/third_party/WebKit/Source/core/frame/RemoteFrameView.h
@@ -28,14 +28,10 @@
   }
 
   void dispose() override;
-
   // Override to notify remote frame that its viewport size has changed.
   void frameRectsChanged() override;
-
   void invalidateRect(const IntRect&) override;
-
   void setFrameRect(const IntRect&) override;
-
   void hide() override;
   void show() override;
   void setParentVisible(bool) override;
@@ -45,11 +41,15 @@
  private:
   explicit RemoteFrameView(RemoteFrame*);
 
+  void updateRemoteViewportIntersection();
+
   // The properties and handling of the cycle between RemoteFrame
   // and its RemoteFrameView corresponds to that between LocalFrame
   // and FrameView. Please see the FrameView::m_frame comment for
   // details.
   Member<RemoteFrame> m_remoteFrame;
+
+  IntRect m_lastViewportIntersection;
 };
 
 DEFINE_TYPE_CASTS(RemoteFrameView,
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index e0b1604..9a5a774a 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1431,6 +1431,10 @@
     BaseWithNewlinesInTarget = 1761,
     BaseWithOpenBracketInTarget = 1762,
     BaseWouldBeBlockedByDefaultSrc = 1763,
+    V8AssigmentExpressionLHSIsCallInSloppy = 1764,
+    V8AssigmentExpressionLHSIsCallInStrict = 1765,
+    V8PromiseConstructorReturnedUndefined = 1766,
+    FormSubmittedWithUnclosedFormControl = 1767,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
index ba4a446..65461fc8 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
@@ -58,7 +58,8 @@
       m_willValidate(true),
       m_isValid(true),
       m_validityIsDirty(false),
-      m_wasFocusedByMouse(false) {
+      m_wasFocusedByMouse(false),
+      m_blocksFormSubmission(false) {
   setHasCustomStyleCallbacks();
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLFormControlElement.h b/third_party/WebKit/Source/core/html/HTMLFormControlElement.h
index 3902398..308b763 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormControlElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLFormControlElement.h
@@ -132,6 +132,9 @@
   FormAssociated* toFormAssociatedOrNull() override { return this; };
   void associateWith(HTMLFormElement*) override;
 
+  bool blocksFormSubmission() const { return m_blocksFormSubmission; }
+  void setBlocksFormSubmission(bool value) { m_blocksFormSubmission = value; }
+
  protected:
   HTMLFormControlElement(const QualifiedName& tagName, Document&);
 
@@ -207,6 +210,7 @@
   bool m_validityIsDirty : 1;
 
   bool m_wasFocusedByMouse : 1;
+  bool m_blocksFormSubmission : 1;
 };
 
 inline bool isHTMLFormControlElement(const Element& element) {
diff --git a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
index 8385afd..be254a2cb 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
@@ -291,6 +291,26 @@
     return;
   }
 
+  // https://github.com/whatwg/html/issues/2253
+  for (const auto& element : listedElements()) {
+    if (element->isFormControlElement() &&
+        toHTMLFormControlElement(element)->blocksFormSubmission()) {
+      UseCounter::count(document(),
+                        UseCounter::FormSubmittedWithUnclosedFormControl);
+      if (RuntimeEnabledFeatures::unclosedFormControlIsInvalidEnabled()) {
+        String tagName = toHTMLFormControlElement(element)->tagName();
+        document().addConsoleMessage(ConsoleMessage::create(
+            SecurityMessageSource, ErrorMessageLevel,
+            "Form submission failed, as the <" + tagName + "> element named "
+                "'" + element->name() + "' was implicitly closed by reaching "
+                "the end of the file. Please add an explicit end tag "
+                "('</" + tagName + ">')"));
+        dispatchEvent(Event::create(EventTypeNames::error));
+        return;
+      }
+    }
+  }
+
   bool skipValidation = !document().page() || noValidate();
   DCHECK(event);
   if (submitButton && submitButton->formNoValidate())
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLElementStack.cpp b/third_party/WebKit/Source/core/html/parser/HTMLElementStack.cpp
index 29b41c8..e6f23d6e 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLElementStack.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLElementStack.cpp
@@ -31,6 +31,8 @@
 #include "core/SVGNames.h"
 #include "core/dom/Element.h"
 #include "core/html/HTMLElement.h"
+#include "core/html/HTMLFormControlElement.h"
+#include "core/html/HTMLSelectElement.h"
 
 namespace blink {
 
@@ -163,8 +165,11 @@
   m_stackDepth = 0;
   while (m_top) {
     Node& node = *topNode();
-    if (node.isElementNode())
+    if (node.isElementNode()) {
       toElement(node).finishParsingChildren();
+      if (isHTMLSelectElement(node))
+        toHTMLFormControlElement(node).setBlocksFormSubmission(true);
+    }
     m_top = m_top->releaseNext();
   }
 }
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
index 16fb629..b7cec906 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
@@ -37,6 +37,7 @@
 #include "core/dom/DocumentFragment.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/frame/UseCounter.h"
+#include "core/html/HTMLFormControlElement.h"
 #include "core/html/HTMLFormElement.h"
 #include "core/html/HTMLTemplateElement.h"
 #include "core/html/parser/AtomicHTMLToken.h"
@@ -2430,17 +2431,21 @@
       defaultForInTableText();
       processEndOfFile(token);
       return;
-    case TextMode:
+    case TextMode: {
       parseError(token);
       if (m_tree.currentStackItem()->hasTagName(scriptTag)) {
         // Mark the script element as "already started".
         DVLOG(1) << "Not implemented.";
       }
+      Element* el = m_tree.openElements()->top();
+      if (isHTMLTextAreaElement(el))
+        toHTMLFormControlElement(el)->setBlocksFormSubmission(true);
       m_tree.openElements()->pop();
       ASSERT(m_originalInsertionMode != TextMode);
       setInsertionMode(m_originalInsertionMode);
       processEndOfFile(token);
       return;
+    }
     case TemplateContentsMode:
       if (processEndOfFileForInTemplateContents(token))
         return;
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.cpp b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.cpp
index d23ba61..aa8d081 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.cpp
@@ -92,9 +92,17 @@
          threadSafeMatch(tagName, MathMLNames::mtextTag);
 }
 
+static bool tokenExitsInSelect(const CompactHTMLToken& token) {
+  // https://html.spec.whatwg.org/#parsing-main-inselect
+  const String& tagName = token.data();
+  return threadSafeMatch(tagName, inputTag) ||
+         threadSafeMatch(tagName, keygenTag) ||
+         threadSafeMatch(tagName, textareaTag);
+}
+
 HTMLTreeBuilderSimulator::HTMLTreeBuilderSimulator(
     const HTMLParserOptions& options)
-    : m_options(options) {
+    : m_options(options), m_inSelectInsertionMode(false) {
   m_namespaceStack.push_back(HTML);
 }
 
@@ -140,20 +148,38 @@
       if (threadSafeMatch(tagName, textareaTag) ||
           threadSafeMatch(tagName, titleTag)) {
         tokenizer->setState(HTMLTokenizer::RCDATAState);
-      } else if (threadSafeMatch(tagName, plaintextTag)) {
-        tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
       } else if (threadSafeMatch(tagName, scriptTag)) {
         tokenizer->setState(HTMLTokenizer::ScriptDataState);
         simulatedToken = ScriptStart;
-      } else if (threadSafeMatch(tagName, styleTag) ||
-                 threadSafeMatch(tagName, iframeTag) ||
-                 threadSafeMatch(tagName, xmpTag) ||
-                 (threadSafeMatch(tagName, noembedTag) &&
-                  m_options.pluginsEnabled) ||
-                 threadSafeMatch(tagName, noframesTag) ||
-                 (threadSafeMatch(tagName, noscriptTag) &&
-                  m_options.scriptEnabled)) {
-        tokenizer->setState(HTMLTokenizer::RAWTEXTState);
+      } else if (!m_inSelectInsertionMode) {
+        // If we're in the "in select" insertion mode, all of these tags are
+        // ignored, so we shouldn't change the tokenizer state:
+        // https://html.spec.whatwg.org/#parsing-main-inselect
+        if (threadSafeMatch(tagName, plaintextTag) &&
+            !m_inSelectInsertionMode) {
+          tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
+        } else if (threadSafeMatch(tagName, styleTag) ||
+                   threadSafeMatch(tagName, iframeTag) ||
+                   threadSafeMatch(tagName, xmpTag) ||
+                   (threadSafeMatch(tagName, noembedTag) &&
+                    m_options.pluginsEnabled) ||
+                   threadSafeMatch(tagName, noframesTag) ||
+                   (threadSafeMatch(tagName, noscriptTag) &&
+                    m_options.scriptEnabled)) {
+          tokenizer->setState(HTMLTokenizer::RAWTEXTState);
+        }
+      }
+
+      // We need to track whether we're in the "in select" insertion mode
+      // in order to determine whether '<plaintext>' will put the tokenizer
+      // into PLAINTEXTState, and whether '<xmp>' and others will consume
+      // textual content.
+      //
+      // https://html.spec.whatwg.org/#parsing-main-inselect
+      if (threadSafeMatch(tagName, selectTag)) {
+        m_inSelectInsertionMode = true;
+      } else if (m_inSelectInsertionMode && tokenExitsInSelect(token)) {
+        m_inSelectInsertionMode = false;
       }
     }
   }
@@ -169,12 +195,15 @@
         (m_namespaceStack.contains(SVG) && m_namespaceStack.back() == HTML &&
          tokenExitsSVG(token)) ||
         (m_namespaceStack.contains(MathML) && m_namespaceStack.back() == HTML &&
-         tokenExitsMath(token)))
+         tokenExitsMath(token))) {
       m_namespaceStack.pop_back();
+    }
     if (threadSafeMatch(tagName, scriptTag)) {
       if (!inForeignContent())
         tokenizer->setState(HTMLTokenizer::DataState);
       return ScriptEnd;
+    } else if (threadSafeMatch(tagName, selectTag)) {
+      m_inSelectInsertionMode = false;
     }
   }
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.h b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.h
index d8027db..420eb21 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilderSimulator.h
@@ -61,6 +61,7 @@
 
   HTMLParserOptions m_options;
   State m_namespaceStack;
+  bool m_inSelectInsertionMode;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/IntersectionGeometry.cpp b/third_party/WebKit/Source/core/layout/IntersectionGeometry.cpp
index 8274879..d8c9a89 100644
--- a/third_party/WebKit/Source/core/layout/IntersectionGeometry.cpp
+++ b/third_party/WebKit/Source/core/layout/IntersectionGeometry.cpp
@@ -109,13 +109,10 @@
 }
 
 void IntersectionGeometry::initializeRootRect() {
-  // TODO(szager): In OOPIF, m_root will be the LayoutView of the
-  // localFrameRoot().  Once viewport intersection support lands,
-  // add a call to mapToVisualRectInAncestorSpace to map the rect up to
-  // top-level frame coordinates.
   if (m_root->isLayoutView()) {
     m_rootRect =
         LayoutRect(toLayoutView(m_root)->frameView()->visibleContentRect());
+    m_root->mapToVisualRectInAncestorSpace(nullptr, m_rootRect);
   } else if (m_root->isBox() && m_root->hasOverflowClip()) {
     m_rootRect = LayoutRect(toLayoutBox(m_root)->contentBoxRect());
   } else {
@@ -145,11 +142,10 @@
 void IntersectionGeometry::clipToRoot() {
   // Map and clip rect into root element coordinates.
   // TODO(szager): the writing mode flipping needs a test.
-  // TODO(szager): Once the OOPIF viewport intersection code lands,
-  // use nullptr for ancestor to map to the top frame.
   LayoutBox* ancestor = toLayoutBox(m_root);
   m_doesIntersect = m_target->mapToVisualRectInAncestorSpace(
-      ancestor, m_intersectionRect, EdgeInclusive);
+      (rootIsImplicit() ? nullptr : ancestor), m_intersectionRect,
+      EdgeInclusive);
   if (ancestor && ancestor->hasOverflowClip())
     m_intersectionRect.move(-ancestor->scrolledContentOffset());
   if (!m_doesIntersect)
@@ -169,14 +165,10 @@
 }
 
 void IntersectionGeometry::mapRootRectToRootFrameCoordinates() {
-  Document& rootDocument = m_root->document();
-  if (!rootIsImplicit())
-    mapRectUpToDocument(m_rootRect, *m_root, rootDocument);
-  // TODO(szager): When OOPIF support lands, this scroll offset adjustment
-  // will probably be wrong.
-  LayoutSize scrollPosition =
-      LayoutSize(rootDocument.view()->getScrollOffset());
-  m_rootRect.move(-scrollPosition);
+  m_root->frameView()->mapQuadToAncestorFrameIncludingScrollOffset(
+      m_rootRect, m_root,
+      rootIsImplicit() ? nullptr : m_root->document().layoutView(),
+      UseTransforms | ApplyContainerFlip);
 }
 
 void IntersectionGeometry::mapIntersectionRectToTargetFrameCoordinates() {
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index 640871b..58d135e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -343,6 +343,8 @@
       transformState.move(parentDocLayoutItem.contentBoxOffset());
 
       parentDocLayoutItem.mapLocalToAncestor(ancestor, transformState, mode);
+    } else {
+      frameView()->applyTransformForTopFrameSpace(transformState);
     }
   }
 }
@@ -463,16 +465,15 @@
     rect.move(offsetForFixedPosition(true));
 
   // Apply our transform if we have one (because of full page zooming).
-  if (!ancestor && layer() && layer()->transform())
+  if (layer() && layer()->transform())
     rect = layer()->transform()->mapRect(rect);
 
-  ASSERT(ancestor);
   if (ancestor == this)
     return true;
 
   Element* owner = document().localOwner();
   if (!owner)
-    return true;
+    return frameView()->mapToVisualRectInTopFrameSpace(rect);
 
   if (LayoutBox* obj = owner->layoutBox()) {
     if (!(mode & InputIsInFrameCoordinates)) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.h b/third_party/WebKit/Source/core/layout/LayoutView.h
index c683ff2..4faa7b0 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.h
+++ b/third_party/WebKit/Source/core/layout/LayoutView.h
@@ -114,6 +114,9 @@
 
   FrameView* frameView() const { return m_frameView; }
 
+  // |ancestor| can be nullptr, which will map the rect to the main frame's
+  // space, even if the main frame is remote (or has intermediate remote
+  // frames in the chain).
   bool mapToVisualRectInAncestorSpace(const LayoutBoxModelObject* ancestor,
                                       LayoutRect&,
                                       MapCoordinatesFlags,
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
index 00ac7f14..ed4ae6a 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
@@ -38,8 +38,7 @@
 
 namespace blink {
 
-ApplicationCache::ApplicationCache(LocalFrame* frame)
-    : ContextLifecycleObserver(frame->document()) {
+ApplicationCache::ApplicationCache(LocalFrame* frame) : DOMWindowClient(frame) {
   ApplicationCacheHost* cacheHost = applicationCacheHost();
   if (cacheHost)
     cacheHost->setApplicationCache(this);
@@ -47,12 +46,7 @@
 
 DEFINE_TRACE(ApplicationCache) {
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
-}
-
-void ApplicationCache::contextDestroyed() {
-  if (ApplicationCacheHost* cacheHost = applicationCacheHost())
-    cacheHost->setApplicationCache(0);
+  DOMWindowClient::trace(visitor);
 }
 
 ApplicationCacheHost* ApplicationCache::applicationCacheHost() const {
@@ -98,9 +92,7 @@
 }
 
 ExecutionContext* ApplicationCache::getExecutionContext() const {
-  if (frame())
-    return frame()->document();
-  return 0;
+  return frame() ? frame()->document() : nullptr;
 }
 
 const AtomicString& ApplicationCache::toEventType(
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
index 50cbcbc..b5f5d334 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
@@ -38,7 +38,7 @@
 class LocalFrame;
 
 class ApplicationCache final : public EventTargetWithInlineData,
-                               public ContextLifecycleObserver {
+                               public DOMWindowClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(ApplicationCache);
 
@@ -48,8 +48,6 @@
   }
   ~ApplicationCache() override {}
 
-  void contextDestroyed() override;
-
   unsigned short status() const;
   void update(ExceptionState&);
   void swapCache(ExceptionState&);
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
index b521bd9b6..ad39760 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
@@ -260,11 +260,12 @@
     const String& errorURL,
     int errorStatus,
     const String& errorMessage) {
-  if (!m_domApplicationCache)
+  // Don't dispatch an event if the window is detached.
+  if (!m_domApplicationCache || !m_domApplicationCache->domWindow())
     return;
 
   const AtomicString& eventType = ApplicationCache::toEventType(id);
-  if (eventType.isEmpty() || !m_domApplicationCache->getExecutionContext())
+  if (eventType.isEmpty())
     return;
   Event* event = nullptr;
   if (id == kProgressEvent) {
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
index 7dfca3c..84060c8 100644
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
@@ -36,7 +36,7 @@
           &FirstMeaningfulPaintDetector::networkStableTimerFired) {}
 
 Document* FirstMeaningfulPaintDetector::document() {
-  return m_paintTiming->host();
+  return m_paintTiming->supplementable();
 }
 
 // Computes "layout significance" (http://goo.gl/rytlPL) of a layout operation.
diff --git a/third_party/WebKit/Source/core/paint/PaintTiming.cpp b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
index 25ef6f72..7edb7876 100644
--- a/third_party/WebKit/Source/core/paint/PaintTiming.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
@@ -123,12 +123,12 @@
       m_fmpDetector(new FirstMeaningfulPaintDetector(this)) {}
 
 LocalFrame* PaintTiming::frame() const {
-  return host()->frame();
+  return supplementable()->frame();
 }
 
 void PaintTiming::notifyPaintTimingChanged() {
-  if (host()->loader())
-    host()->loader()->didChangePerformanceTiming();
+  if (supplementable()->loader())
+    supplementable()->loader()->didChangePerformanceTiming();
 }
 
 void PaintTiming::setFirstPaint(double stamp) {
diff --git a/third_party/WebKit/Source/core/timing/DOMWindowPerformance.cpp b/third_party/WebKit/Source/core/timing/DOMWindowPerformance.cpp
index 180b484..997bbe3 100644
--- a/third_party/WebKit/Source/core/timing/DOMWindowPerformance.cpp
+++ b/third_party/WebKit/Source/core/timing/DOMWindowPerformance.cpp
@@ -41,7 +41,7 @@
 
 Performance* DOMWindowPerformance::performance() {
   if (!m_performance)
-    m_performance = Performance::create(host()->frame());
+    m_performance = Performance::create(supplementable()->frame());
   return m_performance.get();
 }
 
diff --git a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
index 5d84c6a..57c952d 100644
--- a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
+++ b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
@@ -70,15 +70,15 @@
   }
 
   // If detached from frame, do not allow sending a Beacon.
-  if (!host()->frame())
+  if (!supplementable()->frame())
     return false;
 
   return true;
 }
 
 int NavigatorBeacon::maxAllowance() const {
-  DCHECK(host()->frame());
-  const Settings* settings = host()->frame()->settings();
+  DCHECK(supplementable()->frame());
+  const Settings* settings = supplementable()->frame()->settings();
   if (settings) {
     int maxAllowed = settings->getMaxBeaconTransmission();
     if (maxAllowed < m_transmittedBytes)
@@ -118,7 +118,7 @@
   bool allowed;
 
   if (data.isArrayBufferView()) {
-    allowed = PingLoader::sendBeacon(host()->frame(), allowance, url,
+    allowed = PingLoader::sendBeacon(supplementable()->frame(), allowance, url,
                                      data.getAsArrayBufferView(), bytes);
   } else if (data.isBlob()) {
     Blob* blob = data.getAsBlob();
@@ -134,17 +134,17 @@
         return false;
       }
     }
-    allowed =
-        PingLoader::sendBeacon(host()->frame(), allowance, url, blob, bytes);
+    allowed = PingLoader::sendBeacon(supplementable()->frame(), allowance, url,
+                                     blob, bytes);
   } else if (data.isString()) {
-    allowed = PingLoader::sendBeacon(host()->frame(), allowance, url,
+    allowed = PingLoader::sendBeacon(supplementable()->frame(), allowance, url,
                                      data.getAsString(), bytes);
   } else if (data.isFormData()) {
-    allowed = PingLoader::sendBeacon(host()->frame(), allowance, url,
+    allowed = PingLoader::sendBeacon(supplementable()->frame(), allowance, url,
                                      data.getAsFormData(), bytes);
   } else {
-    allowed = PingLoader::sendBeacon(host()->frame(), allowance, url, String(),
-                                     bytes);
+    allowed = PingLoader::sendBeacon(supplementable()->frame(), allowance, url,
+                                     String(), bytes);
   }
 
   if (allowed) {
diff --git a/third_party/WebKit/Source/modules/csspaint/WindowPaintWorklet.cpp b/third_party/WebKit/Source/modules/csspaint/WindowPaintWorklet.cpp
index 63d4a4a..3e4e7db 100644
--- a/third_party/WebKit/Source/modules/csspaint/WindowPaintWorklet.cpp
+++ b/third_party/WebKit/Source/modules/csspaint/WindowPaintWorklet.cpp
@@ -34,8 +34,8 @@
 }
 
 PaintWorklet* WindowPaintWorklet::paintWorklet() {
-  if (!m_paintWorklet && host()->frame())
-    m_paintWorklet = PaintWorklet::create(host()->frame());
+  if (!m_paintWorklet && supplementable()->frame())
+    m_paintWorklet = PaintWorklet::create(supplementable()->frame());
   return m_paintWorklet.get();
 }
 
diff --git a/third_party/WebKit/Source/modules/donottrack/NavigatorDoNotTrack.cpp b/third_party/WebKit/Source/modules/donottrack/NavigatorDoNotTrack.cpp
index 108b38a0..ff645af 100644
--- a/third_party/WebKit/Source/modules/donottrack/NavigatorDoNotTrack.cpp
+++ b/third_party/WebKit/Source/modules/donottrack/NavigatorDoNotTrack.cpp
@@ -62,7 +62,7 @@
 }
 
 String NavigatorDoNotTrack::doNotTrack() {
-  LocalFrame* frame = host()->frame();
+  LocalFrame* frame = supplementable()->frame();
   if (!frame || !frame->loader().client())
     return String();
   return frame->loader().client()->doNotTrackValue();
diff --git a/third_party/WebKit/Source/modules/geolocation/NavigatorGeolocation.cpp b/third_party/WebKit/Source/modules/geolocation/NavigatorGeolocation.cpp
index 8be08ed..2cd4efe 100644
--- a/third_party/WebKit/Source/modules/geolocation/NavigatorGeolocation.cpp
+++ b/third_party/WebKit/Source/modules/geolocation/NavigatorGeolocation.cpp
@@ -52,8 +52,8 @@
 }
 
 Geolocation* NavigatorGeolocation::geolocation() {
-  if (!m_geolocation && host()->frame())
-    m_geolocation = Geolocation::create(host()->frame()->document());
+  if (!m_geolocation && supplementable()->frame())
+    m_geolocation = Geolocation::create(supplementable()->frame()->document());
   return m_geolocation;
 }
 
diff --git a/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp b/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
index 0e998ff3..06b5b60 100644
--- a/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
+++ b/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
@@ -88,10 +88,10 @@
 }
 
 InstalledAppController* NavigatorInstalledApp::controller() {
-  if (!host()->frame())
+  if (!supplementable()->frame())
     return nullptr;
 
-  return InstalledAppController::from(*host()->frame());
+  return InstalledAppController::from(*supplementable()->frame());
 }
 
 const char* NavigatorInstalledApp::supplementName() {
diff --git a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.cpp b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.cpp
index 54ac3bdb..099ab4b 100644
--- a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.cpp
+++ b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.cpp
@@ -128,18 +128,13 @@
   return false;
 }
 
-NavigatorContentUtils* NavigatorContentUtils::from(LocalFrame& frame) {
+NavigatorContentUtils* NavigatorContentUtils::from(Navigator& navigator) {
   return static_cast<NavigatorContentUtils*>(
-      Supplement<LocalFrame>::from(frame, supplementName()));
+      Supplement<Navigator>::from(navigator, supplementName()));
 }
 
 NavigatorContentUtils::~NavigatorContentUtils() {}
 
-NavigatorContentUtils* NavigatorContentUtils::create(
-    NavigatorContentUtilsClient* client) {
-  return new NavigatorContentUtils(client);
-}
-
 void NavigatorContentUtils::registerProtocolHandler(
     Navigator& navigator,
     const String& scheme,
@@ -164,9 +159,8 @@
                         ? UseCounter::RegisterProtocolHandlerSecureOrigin
                         : UseCounter::RegisterProtocolHandlerInsecureOrigin);
 
-  NavigatorContentUtils::from(*navigator.frame())
-      ->client()
-      ->registerProtocolHandler(scheme, document->completeURL(url), title);
+  NavigatorContentUtils::from(navigator)->client()->registerProtocolHandler(
+      scheme, document->completeURL(url), title);
 }
 
 static String customHandlersStateString(
@@ -210,7 +204,7 @@
     return declined;
 
   return customHandlersStateString(
-      NavigatorContentUtils::from(*navigator.frame())
+      NavigatorContentUtils::from(navigator)
           ->client()
           ->isProtocolHandlerRegistered(scheme, document->completeURL(url)));
 }
@@ -232,25 +226,24 @@
   if (!verifyCustomHandlerScheme(scheme, exceptionState))
     return;
 
-  NavigatorContentUtils::from(*navigator.frame())
-      ->client()
-      ->unregisterProtocolHandler(scheme, document->completeURL(url));
+  NavigatorContentUtils::from(navigator)->client()->unregisterProtocolHandler(
+      scheme, document->completeURL(url));
 }
 
 DEFINE_TRACE(NavigatorContentUtils) {
   visitor->trace(m_client);
-  Supplement<LocalFrame>::trace(visitor);
+  Supplement<Navigator>::trace(visitor);
 }
 
 const char* NavigatorContentUtils::supplementName() {
   return "NavigatorContentUtils";
 }
 
-void provideNavigatorContentUtilsTo(LocalFrame& frame,
-                                    NavigatorContentUtilsClient* client) {
-  NavigatorContentUtils::provideTo(frame,
-                                   NavigatorContentUtils::supplementName(),
-                                   NavigatorContentUtils::create(client));
+void NavigatorContentUtils::provideTo(Navigator& navigator,
+                                      NavigatorContentUtilsClient* client) {
+  Supplement<Navigator>::provideTo(
+      navigator, NavigatorContentUtils::supplementName(),
+      new NavigatorContentUtils(navigator, client));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.h b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.h
index 4ee3a80..5298a38 100644
--- a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.h
+++ b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtils.h
@@ -27,7 +27,7 @@
 #ifndef NavigatorContentUtils_h
 #define NavigatorContentUtils_h
 
-#include "core/frame/LocalFrame.h"
+#include "core/frame/Navigator.h"
 #include "modules/ModulesExport.h"
 #include "modules/navigatorcontentutils/NavigatorContentUtilsClient.h"
 #include "platform/Supplementable.h"
@@ -37,18 +37,17 @@
 namespace blink {
 
 class ExceptionState;
-class LocalFrame;
 class Navigator;
 
 class MODULES_EXPORT NavigatorContentUtils final
     : public GarbageCollectedFinalized<NavigatorContentUtils>,
-      public Supplement<LocalFrame> {
+      public Supplement<Navigator> {
   USING_GARBAGE_COLLECTED_MIXIN(NavigatorContentUtils);
 
  public:
   virtual ~NavigatorContentUtils();
 
-  static NavigatorContentUtils* from(LocalFrame&);
+  static NavigatorContentUtils* from(Navigator&);
   static const char* supplementName();
 
   static void registerProtocolHandler(Navigator&,
@@ -65,7 +64,7 @@
                                         const String& url,
                                         ExceptionState&);
 
-  static NavigatorContentUtils* create(NavigatorContentUtilsClient*);
+  static void provideTo(Navigator&, NavigatorContentUtilsClient*);
 
   DECLARE_VIRTUAL_TRACE();
 
@@ -74,8 +73,9 @@
   }
 
  private:
-  explicit NavigatorContentUtils(NavigatorContentUtilsClient* client)
-      : m_client(client) {}
+  NavigatorContentUtils(Navigator& navigator,
+                        NavigatorContentUtilsClient* client)
+      : Supplement<Navigator>(navigator), m_client(client) {}
 
   NavigatorContentUtilsClient* client() { return m_client.get(); }
 
diff --git a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtilsClient.h b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtilsClient.h
index e8cb4e0..afb8e11d 100644
--- a/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtilsClient.h
+++ b/third_party/WebKit/Source/modules/navigatorcontentutils/NavigatorContentUtilsClient.h
@@ -34,8 +34,6 @@
 
 namespace blink {
 
-class LocalFrame;
-
 class NavigatorContentUtilsClient
     : public GarbageCollectedFinalized<NavigatorContentUtilsClient> {
  public:
@@ -57,10 +55,6 @@
   DEFINE_INLINE_VIRTUAL_TRACE() {}
 };
 
-MODULES_EXPORT void provideNavigatorContentUtilsTo(
-    LocalFrame&,
-    NavigatorContentUtilsClient*);
-
 }  // namespace blink
 
 #endif  // NavigatorContentUtilsClient_h
diff --git a/third_party/WebKit/Source/modules/navigatorcontentutils/testing/InternalsNavigatorContentUtils.cpp b/third_party/WebKit/Source/modules/navigatorcontentutils/testing/InternalsNavigatorContentUtils.cpp
index 08a79d0..147e713 100644
--- a/third_party/WebKit/Source/modules/navigatorcontentutils/testing/InternalsNavigatorContentUtils.cpp
+++ b/third_party/WebKit/Source/modules/navigatorcontentutils/testing/InternalsNavigatorContentUtils.cpp
@@ -5,6 +5,7 @@
 #include "InternalsNavigatorContentUtils.h"
 
 #include "core/dom/Document.h"
+#include "core/frame/LocalDOMWindow.h"
 #include "core/testing/Internals.h"
 #include "modules/navigatorcontentutils/NavigatorContentUtils.h"
 #include "modules/navigatorcontentutils/testing/NavigatorContentUtilsClientMock.h"
@@ -16,7 +17,7 @@
     Document* document) {
   ASSERT(document && document->page());
   NavigatorContentUtils* navigatorContentUtils =
-      NavigatorContentUtils::from(*document->frame());
+      NavigatorContentUtils::from(*document->domWindow()->navigator());
   navigatorContentUtils->setClientForTest(
       NavigatorContentUtilsClientMock::create());
 }
diff --git a/third_party/WebKit/Source/modules/storage/DOMWindowStorage.cpp b/third_party/WebKit/Source/modules/storage/DOMWindowStorage.cpp
index 2394a3b..190f62b 100644
--- a/third_party/WebKit/Source/modules/storage/DOMWindowStorage.cpp
+++ b/third_party/WebKit/Source/modules/storage/DOMWindowStorage.cpp
@@ -56,10 +56,10 @@
 
 Storage* DOMWindowStorage::sessionStorage(
     ExceptionState& exceptionState) const {
-  if (!host()->frame())
+  if (!supplementable()->frame())
     return nullptr;
 
-  Document* document = host()->frame()->document();
+  Document* document = supplementable()->frame()->document();
   DCHECK(document);
   String accessDeniedMessage = "Access is denied for this document.";
   if (!document->getSecurityOrigin()->canAccessLocalStorage()) {
@@ -99,10 +99,10 @@
 }
 
 Storage* DOMWindowStorage::localStorage(ExceptionState& exceptionState) const {
-  if (!host()->frame())
+  if (!supplementable()->frame())
     return nullptr;
 
-  Document* document = host()->frame()->document();
+  Document* document = supplementable()->frame()->document();
   DCHECK(document);
   String accessDeniedMessage = "Access is denied for this document.";
   if (!document->getSecurityOrigin()->canAccessLocalStorage()) {
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
index 2841674..c29f840 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
@@ -71,7 +71,7 @@
 }
 
 VRController* NavigatorVR::controller() {
-  if (!host()->frame())
+  if (!supplementable()->frame())
     return 0;
 
   if (!m_controller) {
@@ -82,7 +82,8 @@
 }
 
 Document* NavigatorVR::document() {
-  return host()->frame() ? host()->frame()->document() : nullptr;
+  return supplementable()->frame() ? supplementable()->frame()->document()
+                                   : nullptr;
 }
 
 DEFINE_TRACE(NavigatorVR) {
@@ -104,17 +105,17 @@
 }
 
 void NavigatorVR::enqueueVREvent(VRDisplayEvent* event) {
-  if (host()->frame()) {
-    host()->frame()->domWindow()->enqueueWindowEvent(event);
+  if (supplementable()->frame()) {
+    supplementable()->frame()->domWindow()->enqueueWindowEvent(event);
   }
 }
 
 void NavigatorVR::dispatchVRGestureEvent(VRDisplayEvent* event) {
-  if (!(host()->frame()))
+  if (!(supplementable()->frame()))
     return;
   UserGestureIndicator gestureIndicator(
       DocumentUserGestureToken::create(document()));
-  LocalDOMWindow* window = host()->frame()->domWindow();
+  LocalDOMWindow* window = supplementable()->frame()->domWindow();
   DCHECK(window);
   event->setTarget(window);
   window->dispatchEvent(event);
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 4098586..b567a242 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1863,8 +1863,7 @@
     "//cc/blink",
     "//device/base/synchronization",
     "//mojo/common:test_common_custom_types_blink",
-    "//mojo/edk/embedder:headers",
-    "//mojo/edk/test:test_support",
+    "//mojo/edk/system",
     "//mojo/public/cpp/bindings/tests:for_blink_tests",
     "//skia",
     "//testing/gmock",
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 35fd6e6..c509438 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -285,3 +285,4 @@
 BackgroundVideoTrackOptimization status=stable
 PerformancePaintTiming status=test
 HideNonceContentAttribute status=experimental
+UnclosedFormControlIsInvalid status=experimental
diff --git a/third_party/WebKit/Source/platform/Supplementable.h b/third_party/WebKit/Source/platform/Supplementable.h
index 6ec435c8..ad7fd69 100644
--- a/third_party/WebKit/Source/platform/Supplementable.h
+++ b/third_party/WebKit/Source/platform/Supplementable.h
@@ -99,31 +99,33 @@
   // All Supplement objects should be instantiated with m_host.
   Supplement() {}
 
-  explicit Supplement(T& host) : m_host(&host) {}
+  explicit Supplement(T& supplementable) : m_supplementable(&supplementable) {}
 
   // Supplementable and its supplements live and die together.
-  // Thus host() should never return null (if the default constructor
+  // Thus supplementable() should never return null (if the default constructor
   // is completely removed).
-  T* host() const { return m_host; }
+  T* supplementable() const { return m_supplementable; }
 
-  static void provideTo(Supplementable<T>& host,
+  static void provideTo(Supplementable<T>& supplementable,
                         const char* key,
                         Supplement<T>* supplement) {
-    host.provideSupplement(key, supplement);
+    supplementable.provideSupplement(key, supplement);
   }
 
-  static Supplement<T>* from(Supplementable<T>& host, const char* key) {
-    return host.requireSupplement(key);
+  static Supplement<T>* from(Supplementable<T>& supplementable,
+                             const char* key) {
+    return supplementable.requireSupplement(key);
   }
 
-  static Supplement<T>* from(Supplementable<T>* host, const char* key) {
-    return host ? host->requireSupplement(key) : 0;
+  static Supplement<T>* from(Supplementable<T>* supplementable,
+                             const char* key) {
+    return supplementable ? supplementable->requireSupplement(key) : 0;
   }
 
-  DEFINE_INLINE_VIRTUAL_TRACE() { visitor->trace(m_host); }
+  DEFINE_INLINE_VIRTUAL_TRACE() { visitor->trace(m_supplementable); }
 
  private:
-  Member<T> m_host;
+  Member<T> m_supplementable;
 };
 
 // Supplementable<T> inherits from GarbageCollectedMixin virtually
diff --git a/third_party/WebKit/Source/platform/heap/HeapCompact.cpp b/third_party/WebKit/Source/platform/heap/HeapCompact.cpp
index b31a31b..591caef56 100644
--- a/third_party/WebKit/Source/platform/heap/HeapCompact.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapCompact.cpp
@@ -253,7 +253,6 @@
 HeapCompact::HeapCompact()
     : m_doCompact(false),
       m_gcCountSinceLastCompaction(0),
-      m_threadCount(0),
       m_freeListSize(0),
       m_compactableArenas(0u),
       m_freedPages(0),
@@ -346,7 +345,6 @@
   m_doCompact = true;
   m_freedPages = 0;
   m_freedSize = 0;
-  m_threadCount = state->heap().threads().size();
   m_fixups.reset();
   m_gcCountSinceLastCompaction = 0;
   s_forceCompactionGC = false;
@@ -418,7 +416,6 @@
   if (!m_doCompact)
     return;
 
-  MutexLocker locker(m_mutex);
   if (!m_startCompactionTimeMS)
     m_startCompactionTimeMS = WTF::currentTimeMS();
 }
@@ -427,48 +424,32 @@
   if (!m_doCompact)
     return;
 
-  MutexLocker locker(m_mutex);
-  // Final one clears out.
-  if (!--m_threadCount) {
 #if DEBUG_HEAP_COMPACTION
-    if (m_fixups)
-      m_fixups->dumpDebugStats();
+  if (m_fixups)
+    m_fixups->dumpDebugStats();
 #endif
-    m_fixups.reset();
-    m_doCompact = false;
+  m_fixups.reset();
+  m_doCompact = false;
 
-    double timeForHeapCompaction =
-        WTF::currentTimeMS() - m_startCompactionTimeMS;
-    DEFINE_STATIC_LOCAL(CustomCountHistogram, timeForHeapCompactionHistogram,
-                        ("BlinkGC.TimeForHeapCompaction", 1, 10 * 1000, 50));
-    timeForHeapCompactionHistogram.count(timeForHeapCompaction);
-    m_startCompactionTimeMS = 0;
+  double timeForHeapCompaction = WTF::currentTimeMS() - m_startCompactionTimeMS;
+  DEFINE_STATIC_LOCAL(CustomCountHistogram, timeForHeapCompactionHistogram,
+                      ("BlinkGC.TimeForHeapCompaction", 1, 10 * 1000, 50));
+  timeForHeapCompactionHistogram.count(timeForHeapCompaction);
+  m_startCompactionTimeMS = 0;
 
-    DEFINE_STATIC_LOCAL(
-        CustomCountHistogram, objectSizeFreedByHeapCompaction,
-        ("BlinkGC.ObjectSizeFreedByHeapCompaction", 1, 4 * 1024 * 1024, 50));
-    objectSizeFreedByHeapCompaction.count(m_freedSize / 1024);
+  DEFINE_STATIC_LOCAL(
+      CustomCountHistogram, objectSizeFreedByHeapCompaction,
+      ("BlinkGC.ObjectSizeFreedByHeapCompaction", 1, 4 * 1024 * 1024, 50));
+  objectSizeFreedByHeapCompaction.count(m_freedSize / 1024);
 
 #if DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME
-    LOG_HEAP_COMPACTION_INTERNAL(
-        "Compaction stats: time=%gms, pages freed=%zu, size=%zu\n",
-        timeForHeapCompaction, m_freedPages, m_freedSize);
+  LOG_HEAP_COMPACTION_INTERNAL(
+      "Compaction stats: time=%gms, pages freed=%zu, size=%zu\n",
+      timeForHeapCompaction, m_freedPages, m_freedSize);
 #else
-    LOG_HEAP_COMPACTION("Compaction stats: freed pages=%zu size=%zu\n",
-                        m_freedPages, m_freedSize);
+  LOG_HEAP_COMPACTION("Compaction stats: freed pages=%zu size=%zu\n",
+                      m_freedPages, m_freedSize);
 #endif
-
-    // Compaction has been completed by all participating threads, unblock
-    // them all.
-    m_finished.broadcast();
-  } else {
-    // Letting a thread return here and let it exit its safe point opens up
-    // the possibility of it accessing heaps of other threads that are
-    // still being compacted. It is not in a valid state until objects have
-    // all been moved together, hence all GC-participating threads must
-    // complete compaction together. Grab the condition variable and wait.
-    m_finished.wait(m_mutex);
-  }
 }
 
 void HeapCompact::addCompactingPage(BasePage* page) {
diff --git a/third_party/WebKit/Source/platform/heap/HeapCompact.h b/third_party/WebKit/Source/platform/heap/HeapCompact.h
index e7e13334..5c8e1a88 100644
--- a/third_party/WebKit/Source/platform/heap/HeapCompact.h
+++ b/third_party/WebKit/Source/platform/heap/HeapCompact.h
@@ -139,18 +139,6 @@
   bool m_doCompact;
   size_t m_gcCountSinceLastCompaction;
 
-  // Lock protecting finishedThreadCompaction() signalling.
-  Mutex m_mutex;
-
-  // All threads performing a GC must synchronize on completion
-  // of all heap compactions. Not doing so risks one thread resuming
-  // the mutator, which could perform cross-thread access to a heap
-  // that's still in the process of being compacted.
-  ThreadCondition m_finished;
-
-  // Number of heap threads participating in the compaction.
-  int m_threadCount;
-
   // Last reported freelist size, across all compactable arenas.
   size_t m_freeListSize;
 
diff --git a/third_party/WebKit/Source/platform/testing/DEPS b/third_party/WebKit/Source/platform/testing/DEPS
index c5e2cc8..81f18ea 100644
--- a/third_party/WebKit/Source/platform/testing/DEPS
+++ b/third_party/WebKit/Source/platform/testing/DEPS
@@ -11,5 +11,4 @@
     "+base/test/test_io_thread.h",
     "+cc",
     "+mojo/edk/embedder",
-    "+mojo/edk/test",
 ]
diff --git a/third_party/WebKit/Source/platform/testing/RunAllTests.cpp b/third_party/WebKit/Source/platform/testing/RunAllTests.cpp
index 837dd9f..67bd9c9 100644
--- a/third_party/WebKit/Source/platform/testing/RunAllTests.cpp
+++ b/third_party/WebKit/Source/platform/testing/RunAllTests.cpp
@@ -33,7 +33,7 @@
 #include "base/test/test_io_thread.h"
 #include "base/test/test_suite.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "platform/heap/Heap.h"
 #include "platform/testing/TestingPlatformSupport.h"
 #include <memory>
@@ -56,9 +56,9 @@
 
     mojo::edk::Init();
     base::TestIOThread testIoThread(base::TestIOThread::kAutoStart);
-    std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipcSupport(
-        WTF::wrapUnique(
-            new mojo::edk::test::ScopedIPCSupport(testIoThread.task_runner())));
+    mojo::edk::ScopedIPCSupport ipcSupport(
+        testIoThread.task_runner(),
+        mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
     result = base::LaunchUnitTests(
         argc, argv, base::Bind(runTestSuite, base::Unretained(&testSuite)));
   }
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index e5712bb..3fa95b1 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -57,6 +57,7 @@
 #include "modules/audio_output_devices/AudioOutputDeviceClient.h"
 #include "modules/installedapp/InstalledAppController.h"
 #include "modules/mediastream/UserMediaController.h"
+#include "modules/navigatorcontentutils/NavigatorContentUtils.h"
 #include "modules/presentation/PresentationController.h"
 #include "modules/push_messaging/PushController.h"
 #include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
@@ -1155,8 +1156,9 @@
                      UserMediaClientImpl::create(client->userMediaClient()));
   provideIndexedDBClientTo(frame, IndexedDBClientImpl::create(frame));
   provideLocalFileSystemTo(frame, LocalFileSystemClient::create());
-  provideNavigatorContentUtilsTo(
-      frame, NavigatorContentUtilsClientImpl::create(webFrame));
+  NavigatorContentUtils::provideTo(
+      *frame.domWindow()->navigator(),
+      NavigatorContentUtilsClientImpl::create(webFrame));
 
   ScreenOrientationControllerImpl::provideTo(
       frame, client->webScreenOrientationClient());
diff --git a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
index 93a4c708..c4a072a 100644
--- a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
@@ -12,6 +12,7 @@
 #include "core/layout/api/LayoutItem.h"
 #include "core/layout/api/LayoutPartItem.h"
 #include "platform/exported/WrappedResourceRequest.h"
+#include "platform/geometry/IntRect.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "platform/weborigin/SecurityPolicy.h"
 #include "public/web/WebRemoteFrameClient.h"
@@ -174,6 +175,11 @@
   m_webFrame->client()->frameRectsChanged(frameRect);
 }
 
+void RemoteFrameClientImpl::updateRemoteViewportIntersection(
+    const IntRect& viewportIntersection) {
+  m_webFrame->client()->updateRemoteViewportIntersection(viewportIntersection);
+}
+
 void RemoteFrameClientImpl::advanceFocus(WebFocusType type,
                                          LocalFrame* source) {
   m_webFrame->client()->advanceFocus(type,
diff --git a/third_party/WebKit/Source/web/RemoteFrameClientImpl.h b/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
index 85c5782..8fb019a6 100644
--- a/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
@@ -38,6 +38,7 @@
                           LocalFrame* source) const override;
   void forwardInputEvent(Event*) override;
   void frameRectsChanged(const IntRect& frameRect) override;
+  void updateRemoteViewportIntersection(const IntRect&) override;
   void advanceFocus(WebFocusType, LocalFrame*) override;
   void visibilityChanged(bool visible) override;
   void setHasReceivedUserGesture() override;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index b8812f5..4f686427 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -712,6 +712,17 @@
   }
 }
 
+void WebFrameWidgetImpl::setRemoteViewportIntersection(
+    const WebRect& viewportIntersection) {
+  // Remote viewports are only applicable to local frames with remote ancestors.
+  DCHECK(m_localRoot->parent() && m_localRoot->parent()->isWebRemoteFrame());
+
+  if (m_localRoot->frameView()) {
+    m_localRoot->frameView()->setViewportIntersectionFromParent(
+        viewportIntersection);
+  }
+}
+
 void WebFrameWidgetImpl::handleMouseLeave(LocalFrame& mainFrame,
                                           const WebMouseEvent& event) {
   // FIXME: WebWidget doesn't have the method below.
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
index c99f3a6..6376a986 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
@@ -113,6 +113,7 @@
   void didLosePointerLock() override;
   bool getCompositionCharacterBounds(WebVector<WebRect>& bounds) override;
   void applyReplacementRange(const WebRange&) override;
+  void setRemoteViewportIntersection(const WebRect&) override;
 
   // WebFrameWidget implementation.
   WebLocalFrameImpl* localRoot() const override { return m_localRoot; }
diff --git a/third_party/WebKit/public/web/WebFrameWidget.h b/third_party/WebKit/public/web/WebFrameWidget.h
index 1ff35be..5178df6 100644
--- a/third_party/WebKit/public/web/WebFrameWidget.h
+++ b/third_party/WebKit/public/web/WebFrameWidget.h
@@ -112,9 +112,14 @@
                                  const WebPoint& screenPoint,
                                  WebDragOperation) = 0;
 
-  // Notfies the WebFrameWidget that the system drag and drop operation has
+  // Notifies the WebFrameWidget that the system drag and drop operation has
   // ended.
   virtual void dragSourceSystemDragEnded() = 0;
+
+  // Constrains the viewport intersection for use by IntersectionObserver.
+  // This is needed for out-of-process iframes to know if they are clipped
+  // by ancestor frames in another process.
+  virtual void setRemoteViewportIntersection(const WebRect&) {}
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/web/WebRemoteFrameClient.h b/third_party/WebKit/public/web/WebRemoteFrameClient.h
index d559413..dc0394e5 100644
--- a/third_party/WebKit/public/web/WebRemoteFrameClient.h
+++ b/third_party/WebKit/public/web/WebRemoteFrameClient.h
@@ -42,6 +42,9 @@
 
   virtual void frameRectsChanged(const WebRect&) {}
 
+  virtual void updateRemoteViewportIntersection(
+      const WebRect& viewportIntersection) {}
+
   virtual void visibilityChanged(bool visible) {}
 
   virtual void setHasReceivedUserGesture() {}
diff --git a/tools/ipc_fuzzer/message_replay/replay_process.cc b/tools/ipc_fuzzer/message_replay/replay_process.cc
index c14d7d8..d69f9c1 100644
--- a/tools/ipc_fuzzer/message_replay/replay_process.cc
+++ b/tools/ipc_fuzzer/message_replay/replay_process.cc
@@ -92,8 +92,9 @@
              kMojoIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
 #endif
 
-  mojo_ipc_support_.reset(
-      new mojo::edk::ScopedIPCSupport(io_thread_.task_runner()));
+  mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport(
+      io_thread_.task_runner(),
+      mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST));
   InitializeMojoIPCChannel();
 
   return true;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 63921188..7b3ec48d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -19833,8 +19833,21 @@
   </summary>
 </histogram>
 
+<histogram name="GeolocationDisclosure.PostDisclosureContentSetting"
+    enum="ContentSetting">
+  <owner>benwells@chromium.org</owner>
+  <summary>
+    Records the geolocation content setting for the default search engine's
+    origin after the search geolocation disclosure has been shown and won't be
+    shown again. This metric is only recorded once per client.
+  </summary>
+</histogram>
+
 <histogram name="GeolocationDisclosure.PostDisclosurePermission"
     enum="PermissionStatus">
+  <obsolete>
+    Deprecated 2017/01. Replaced by PostDisclosureContentSetting.
+  </obsolete>
   <owner>benwells@chromium.org</owner>
   <summary>
     Records the geolocation permission for the default search engine's origin
@@ -19843,8 +19856,21 @@
   </summary>
 </histogram>
 
+<histogram name="GeolocationDisclosure.PreDisclosureContentSetting"
+    enum="ContentSetting">
+  <owner>benwells@chromium.org</owner>
+  <summary>
+    Records the geolocation content setting for the default search engine's
+    origin immediately before the search geolocation disclosure has been shown.
+    This metric is only recorded once per client.
+  </summary>
+</histogram>
+
 <histogram name="GeolocationDisclosure.PreDisclosurePermission"
     enum="PermissionStatus">
+  <obsolete>
+    Deprecated 2017/01. Replaced by PreDisclosureContentSetting.
+  </obsolete>
   <owner>benwells@chromium.org</owner>
   <summary>
     Records the geolocation permission for the default search engine's origin
@@ -38901,6 +38927,36 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.ContentSuggestions.Notifications.Actions"
+    enum="ContentSuggestionsNotificationsAction">
+  <owner>sfiera@chromium.org</owner>
+  <summary>
+    Android: The number of actions taken on displayed notifications, either
+    user-initiated (tapping to open or swiping to dismiss), or automatic (hiding
+    due to a change in circumstances).
+  </summary>
+</histogram>
+
+<histogram name="NewTabPage.ContentSuggestions.Notifications.Impressions"
+    enum="ContentSuggestionsNotificationsImpression">
+  <owner>sfiera@chromium.org</owner>
+  <summary>
+    Android: The number of notifications that were shown, keyed by category. One
+    bucket counts the server-provided ARTICLES category; the other counts
+    everything else.
+  </summary>
+</histogram>
+
+<histogram name="NewTabPage.ContentSuggestions.Notifications.OptOuts"
+    enum="ContentSuggestionsNotificationsOptOut">
+  <owner>sfiera@chromium.org</owner>
+  <summary>
+    Android: The number of times a user opted out, keyed by method: IMPLICIT
+    (user ignored enough notifications consecutively that we think they're not
+    interested), or EXPLICIT (via explicit setting).
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.ContentSuggestions.OpenDisposition"
     enum="WindowOpenDisposition">
   <owner>treib@chromium.org</owner>
@@ -44544,6 +44600,22 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PasswordManager.ShouldBlockPasswordForSameOriginButDifferentScheme"
+    enum="BooleanBlocked">
+  <owner>jdoerrie@chromium.org</owner>
+  <summary>
+    This metric is recorded every time Chrome detects a password form
+    submission. The credential might be proposed to be saved, or it might be
+    blocked. It is blocked if the previously detected password form submission
+    was successful, and the current submission is from a URL with the same
+    origin, but a different and insecure scheme. That is, if the previously
+    provisionally saved credential is for https://example.com, a new credential
+    for http://example.com would be blocked. More details can be found in the
+    doc associated with http://crbug.com/571580.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.ShouldShowAutoSignInFirstRunExperience"
     enum="BooleanPending">
   <owner>vasilii@chromium.org</owner>
@@ -49824,16 +49896,6 @@
   </summary>
 </histogram>
 
-<histogram name="Prerender.NoStatePrefetchTTFCP" units="ms">
-  <obsolete>
-    Deprecated December 28, 2016, use Prerender.PrefetchTTFCP instead.
-  </obsolete>
-  <owner>droger@chromium.org</owner>
-  <owner>mattcary@chromium.org</owner>
-  <owner>pasko@chromium.org</owner>
-  <summary>Time to first contentful paint.</summary>
-</histogram>
-
 <histogram name="Prerender.OmniboxNavigationsCouldPrerender">
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -80923,6 +80985,26 @@
   <int value="6" label="Articles"/>
 </enum>
 
+<enum name="ContentSuggestionsNotificationsAction" type="int">
+  <int value="0" label="Tap"/>
+  <int value="1" label="Dismissal"/>
+  <int value="2" label="Hidden (Notification deadline passed)"/>
+  <int value="3" label="Hidden (Suggestion expired)"/>
+  <int value="4" label="Hidden (Chrome became frontmost)"/>
+  <int value="5" label="Hidden (Category disabled)"/>
+  <int value="6" label="Hidden (Content Suggestion Service shut down)"/>
+</enum>
+
+<enum name="ContentSuggestionsNotificationsImpression" type="int">
+  <int value="0" label="Article"/>
+  <int value="1" label="Non-Article"/>
+</enum>
+
+<enum name="ContentSuggestionsNotificationsOptOut" type="int">
+  <int value="0" label="Implicit"/>
+  <int value="1" label="Explicit"/>
+</enum>
+
 <enum name="ContentType" type="int">
   <int value="-1" label="Invalid setting"/>
   <int value="0" label="Cookies setting"/>
@@ -88888,6 +88970,10 @@
   <int value="1761" label="BaseWithNewlinesInTarget"/>
   <int value="1762" label="BaseWithOpenBracketInTarget"/>
   <int value="1763" label="BaseWouldBeBlockedByDefaultSrc"/>
+  <int value="1764" label="V8AssigmentExpressionLHSIsCallInSloppy"/>
+  <int value="1765" label="V8AssigmentExpressionLHSIsCallInStrict"/>
+  <int value="1766" label="V8PromiseConstructorReturnedUndefined"/>
+  <int value="1767" label="FormSubmittedWithUnclosedFormControl"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -114979,16 +115065,12 @@
       label="No prefetch. Warning: do not compare with the load times of
              prefetched pages (bias)."/>
   <suffix name="Warm" label="Prefetch skips revalidation."/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP"/>
   <affected-histogram name="Prerender.PrefetchTTFCP"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="PrerenderPrefetchMainResourceType" separator=".">
   <suffix name="Cacheable" label="Main resource cacheable."/>
   <suffix name="NoStore" label="Main resource no-store."/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Cold"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Reference"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Warm"/>
   <affected-histogram name="Prerender.PrefetchTTFCP.Cold"/>
   <affected-histogram name="Prerender.PrefetchTTFCP.Reference"/>
   <affected-histogram name="Prerender.PrefetchTTFCP.Warm"/>
@@ -115037,13 +115119,6 @@
   <affected-histogram name="Prerender.NoStatePrefetchMainResourceRedirects"/>
   <affected-histogram name="Prerender.NoStatePrefetchResponseTypes"/>
   <affected-histogram name="Prerender.NoStatePrefetchSubResourceRedirects"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Cold.Cacheable"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Cold.NoStore"/>
-  <affected-histogram
-      name="Prerender.NoStatePrefetchTTFCP.Reference.Cacheable"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Reference.NoStore"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Warm.Cacheable"/>
-  <affected-histogram name="Prerender.NoStatePrefetchTTFCP.Warm.NoStore"/>
   <affected-histogram name="Prerender.PageVisitedStatus"/>
   <affected-histogram name="Prerender.PerceivedPLT"/>
   <affected-histogram name="Prerender.PerceivedPLTFirstAfterMiss"/>
diff --git a/ui/events/event.cc b/ui/events/event.cc
index dc212a5f..f17899623 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -44,39 +44,6 @@
 #include "ui/events/keycodes/platform_key_map_win.h"
 #endif
 
-// Support a collection of histograms, perhaps one for each entry in an
-// enumeration. This macro manages a block of pointers, adding to a specific
-// one by its index.
-//
-// A typical instantiation looks something like this:
-//  STATIC_HISTOGRAM_POINTER_GROUP(
-//      GetHistogramNameForIndex(histogram_index),
-//      histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta),
-//      base::Histogram::FactoryGet(
-//          GetHistogramNameForType(histogram_index),
-//          MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
-//          base::HistogramBase::kUmaTargetedHistogramFlag));
-//
-// Though it seems inefficient to generate the name twice, the first
-// instance will be used only for DCHECK builds and the second will
-// execute only during the first access to the given index, after which
-// the pointer is cached and the name never needed again.
-//
-// This is defined in this way so that it can be moved unchanged into
-// base/metrics/histogram_macros.h if it is useful in other files.
-#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index,        \
-                                       constant_maximum,                      \
-                                       histogram_add_method_invocation,       \
-                                       histogram_factory_get_invocation)      \
-  do {                                                                        \
-    static base::subtle::AtomicWord atomic_histograms[constant_maximum];      \
-    DCHECK_LE(0, index);                                                      \
-    DCHECK_LT(index, constant_maximum);                                       \
-    HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \
-                          histogram_add_method_invocation,                    \
-                          histogram_factory_get_invocation);                  \
-  } while (0)
-
 namespace {
 
 std::string EventTypeName(ui::EventType type) {
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index 638bd29f..674685b 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -283,7 +283,14 @@
   if (keyboard_mode_ == FLOATING) {
     NotifyKeyboardBoundsChanging(gfx::Rect());
   } else if (keyboard_mode_ == FULL_WIDTH) {
-    AdjustKeyboardBounds();
+    // TODO(bshe): revisit this logic after we decide to support resize virtual
+    // keyboard.
+    int keyboard_height = GetContainerWindow()->bounds().height();
+    const gfx::Rect& root_bounds = container_->GetRootWindow()->bounds();
+    gfx::Rect new_bounds = root_bounds;
+    new_bounds.set_y(root_bounds.height() - keyboard_height);
+    new_bounds.set_height(keyboard_height);
+    GetContainerWindow()->SetBounds(new_bounds);
     // No animation added, so call ShowAnimationFinished immediately.
     ShowAnimationFinished();
   }
@@ -312,7 +319,6 @@
 void KeyboardController::OnWindowAddedToRootWindow(aura::Window* window) {
   if (!window->GetRootWindow()->HasObserver(this))
     window->GetRootWindow()->AddObserver(this);
-  AdjustKeyboardBounds();
 }
 
 void KeyboardController::OnWindowRemovingFromRootWindow(aura::Window* window,
@@ -505,21 +511,4 @@
     observer.OnKeyboardHidden();
 }
 
-void KeyboardController::AdjustKeyboardBounds() {
-  // When keyboard is floating, no resize is necessary.
-  if (keyboard_mode_ == FLOATING)
-    return;
-
-  if (keyboard_mode_ == FULL_WIDTH) {
-    // TODO(bshe): revisit this logic after we decide to support resize virtual
-    // keyboard.
-    int keyboard_height = GetContainerWindow()->bounds().height();
-    const gfx::Rect& root_bounds = container_->GetRootWindow()->bounds();
-    gfx::Rect new_bounds = root_bounds;
-    new_bounds.set_y(root_bounds.height() - keyboard_height);
-    new_bounds.set_height(keyboard_height);
-    GetContainerWindow()->SetBounds(new_bounds);
-  }
-}
-
 }  // namespace keyboard
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index 2bfe473f..817fa0d 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -159,10 +159,6 @@
   void ShowAnimationFinished();
   void HideAnimationFinished();
 
-  // Called when the keyboard mode is set or the keyboard is moved to another
-  // display.
-  void AdjustKeyboardBounds();
-
   std::unique_ptr<KeyboardUI> ui_;
   KeyboardLayoutDelegate* layout_delegate_;
   std::unique_ptr<aura::Window> container_;
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 6357440a..c2b47f3c 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -299,8 +299,7 @@
   const gfx::Rect& initial_bounds = container->bounds();
   // The container should be positioned at the bottom of screen and has 0
   // height.
-  ASSERT_EQ(0, initial_bounds.height());
-  ASSERT_EQ(screen_bounds.height(), initial_bounds.y());
+  ASSERT_EQ(gfx::Rect(), initial_bounds);
   VerifyKeyboardWindowSize(container, keyboard);
 
   // In FULL_WIDTH mode, attempt to change window width or move window up from
@@ -341,38 +340,6 @@
   VerifyKeyboardWindowSize(container, keyboard);
 }
 
-TEST_F(KeyboardControllerTest, KeyboardSizeMultiRootWindow) {
-  aura::Window* container(controller()->GetContainerWindow());
-  aura::Window* keyboard(ui()->GetKeyboardWindow());
-  gfx::Rect screen_bounds = root_window()->bounds();
-  root_window()->AddChild(container);
-  container->AddChild(keyboard);
-  const gfx::Rect& initial_bounds = container->bounds();
-  // The container should be positioned at the bottom of screen and has 0
-  // height.
-  ASSERT_EQ(0, initial_bounds.height());
-  ASSERT_EQ(screen_bounds.height(), initial_bounds.y());
-  VerifyKeyboardWindowSize(container, keyboard);
-
-  // Adding new root window.
-  std::unique_ptr<aura::WindowTreeHost> secondary_tree_host =
-      base::WrapUnique<aura::WindowTreeHost>(
-          aura::WindowTreeHost::Create(gfx::Rect(0, 0, 1000, 500)));
-  secondary_tree_host->InitHost();
-  EXPECT_EQ(1000, secondary_tree_host->window()->bounds().width());
-  EXPECT_EQ(500, secondary_tree_host->window()->bounds().height());
-
-  // Move the keyboard into the secondary root window.
-  controller()->HideKeyboard(
-      KeyboardController::HideReason::HIDE_REASON_AUTOMATIC);
-  root_window()->RemoveChild(container);
-  secondary_tree_host->window()->AddChild(container);
-
-  const gfx::Rect& new_bounds = container->bounds();
-  EXPECT_EQ(500, new_bounds.y());
-  VerifyKeyboardWindowSize(container, keyboard);
-}
-
 // Tests that tapping/clicking inside the keyboard does not give it focus.
 TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
   keyboard::SetAccessibilityKeyboardEnabled(true);
diff --git a/url/DEPS b/url/DEPS
index c89ac32..946d75f 100644
--- a/url/DEPS
+++ b/url/DEPS
@@ -11,7 +11,6 @@
     "+third_party/icu",
   ],
   "run_all_unittests\.cc": [
-    "+mojo/edk/embedder/embedder.h",
-    "+mojo/edk/test/scoped_ipc_support.h",
+    "+mojo/edk/embedder",
   ],
 }
diff --git a/url/run_all_unittests.cc b/url/run_all_unittests.cc
index c0b306a2..fcafd03 100644
--- a/url/run_all_unittests.cc
+++ b/url/run_all_unittests.cc
@@ -13,7 +13,6 @@
 
 #if !defined(OS_IOS)
 #include "mojo/edk/embedder/embedder.h"  // nogncheck
-#include "mojo/edk/test/scoped_ipc_support.h"  // nogncheck
 #endif
 
 int main(int argc, char** argv) {
@@ -21,10 +20,6 @@
 
 #if !defined(OS_IOS)
   mojo::edk::Init();
-  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipc_support;
-  ipc_support.reset(
-      new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner()));
 #endif
 
   return base::LaunchUnitTests(