diff --git a/DEPS b/DEPS
index 22e321bb..9fce461a 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': '6e1e27b7fccd74d91d9bbc2a22bf55f2725f2a1b',
+  'skia_revision': '8da9e940721168143d6296f578b6d7423de55d69',
   # 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': 'fbffc377e396bd5c6d735f612c0dbcb784e7cd5d',
+  'v8_revision': '5362e552a9186bfec815cacfb469bce66b4655fd',
   # 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.
@@ -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': '2ae07fc28bb3b248baa71647d5cfcefd67e57a84',
+  'catapult_revision': 'faa1c3e0aeca393036f21a3722b75b245bb432c7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -303,7 +303,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '0fd88df93c5dcaf858c57eb7892bd27763f0f0ac',
 
   'src/third_party/re2/src':
-    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + 'db20d46c05900a12539225fe800dd860b14e0061',
+    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + 'ae9cb49a2e2ba95de4f0c6ec5a8afd039996d2c7',
 
   # Used for building libFuzzers (only supports Linux).
   'src/third_party/libFuzzer/src':
diff --git a/android_webview/apk/java/proguard.flags b/android_webview/apk/java/proguard.flags
index c4cf0a4..495d4861 100644
--- a/android_webview/apk/java/proguard.flags
+++ b/android_webview/apk/java/proguard.flags
@@ -70,5 +70,11 @@
 }
 -dontnote com.android.webview.chromium.AwSafeBrowsingApiHandler
 
+#TODO(hush): remove after N release. crbug.com/546762
+-keep class com.android.webview.chromium.ContentSettingsAdapter {
+  public void setDisabledActionModeMenuItems(int);
+  public int getDisabledActionModeMenuItems();
+}
+
 # We strip some unused resources when preprocessing the GMS client libs.
 -dontwarn com.google.android.gms.R**
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index 99dd3fa..0ea0849 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -63,6 +63,7 @@
 import java.io.BufferedWriter;
 import java.io.File;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
@@ -1729,7 +1730,8 @@
         mAwContents.onConfigurationChanged(newConfig);
     }
 
-    @Override
+    //TODO(hush): add override after release.
+    //@Override
     public boolean onDragEvent(final DragEvent event) {
         mFactory.startYourEngines(false);
         if (checkNeedsPost()) {
@@ -2211,7 +2213,15 @@
 
         @Override
         public void super_startActivityForResult(Intent intent, int requestCode) {
-            mWebViewPrivate.super_startActivityForResult(intent, requestCode);
+            // TODO(hush): Use mWebViewPrivate.super_startActivityForResult
+            // after N release. crbug.com/543272.
+            try {
+                Method startActivityForResultMethod =
+                        View.class.getMethod("startActivityForResult", Intent.class, int.class);
+                startActivityForResultMethod.invoke(mWebView, intent, requestCode);
+            } catch (Exception e) {
+                throw new RuntimeException("Invalid reflection", e);
+            }
         }
 
         @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index f4e1cfe..896e9b6 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2356,7 +2356,9 @@
     void startProcessTextIntent(Intent intent) {
         // on Android M, WebView is not able to replace the text with the processed text.
         // So set the readonly flag for M.
-        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
+        // TODO(hush): remove the part about VERSION.CODENAME equality with N, after N release.
+        // crbug.com/543272
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M && !Build.VERSION.CODENAME.equals("N")) {
             intent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, true);
         }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index a49d01c..231091e2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -34,6 +34,9 @@
     private static final String LOGTAG = AwSettings.class.getSimpleName();
     private static final boolean TRACE = false;
 
+    // TODO(hush): Use android.webkit.WebSettings.MENU_ITEM_*. crbug.com/546762.
+    private static final int MENU_ITEM_NONE = 0;
+
     private static final String TAG = "AwSettings";
 
     // This class must be created on the UI thread. Afterwards, it can be
@@ -86,7 +89,7 @@
     private int mMixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW;
 
     private boolean mOffscreenPreRaster;
-    private int mDisabledMenuItems = WebSettings.MENU_ITEM_NONE;
+    private int mDisabledMenuItems = MENU_ITEM_NONE;
 
     // Although this bit is stored on AwSettings it is actually controlled via the CookieManager.
     private boolean mAcceptThirdPartyCookies;
diff --git a/ash/common/system/status_area_widget.cc b/ash/common/system/status_area_widget.cc
index 3c66000c..25b886e4 100644
--- a/ash/common/system/status_area_widget.cc
+++ b/ash/common/system/status_area_widget.cc
@@ -82,6 +82,10 @@
   // Destroy the trays early, causing them to be removed from the view
   // hierarchy. Do not used scoped pointers since we don't want to destroy them
   // in the destructor if Shutdown() is not called (e.g. in tests).
+  // Failure to remove the tray views causes layout crashes during shutdown,
+  // for example http://crbug.com/700122.
+  // TODO(jamescook): Find a better way to avoid the layout problems, fix the
+  // tests and switch to std::unique_ptr. http://crbug.com/700255
   delete web_notification_tray_;
   web_notification_tray_ = nullptr;
   // Must be destroyed after |web_notification_tray_|.
@@ -91,10 +95,14 @@
   ime_menu_tray_ = nullptr;
   delete virtual_keyboard_tray_;
   virtual_keyboard_tray_ = nullptr;
+  delete palette_tray_;
+  palette_tray_ = nullptr;
   delete logout_button_tray_;
   logout_button_tray_ = nullptr;
   delete overview_button_tray_;
   overview_button_tray_ = nullptr;
+  // All child tray views have been removed.
+  DCHECK_EQ(0, GetContentsView()->child_count());
 }
 
 void StatusAreaWidget::SetShelfAlignment(ShelfAlignment alignment) {
diff --git a/ash/common/wm/default_state.cc b/ash/common/wm/default_state.cc
index 8149d7f..dbaf92d 100644
--- a/ash/common/wm/default_state.cc
+++ b/ash/common/wm/default_state.cc
@@ -701,8 +701,9 @@
         WmWindow* docked_container =
             window->GetRootWindow()->GetChildByShellWindowId(
                 kShellWindowId_DockedContainer);
-        ReparentChildWithTransientChildren(window, window->GetParent(),
-                                           docked_container);
+        ReparentChildWithTransientChildren(window->aura_window(),
+                                           window->aura_window()->parent(),
+                                           docked_container->aura_window());
       }
       // Return early because we don't want to update the bounds of the
       // window below; as the bounds are managed by the dock layout.
diff --git a/ash/common/wm/dock/docked_window_layout_manager.cc b/ash/common/wm/dock/docked_window_layout_manager.cc
index c8920b40..d004da9 100644
--- a/ash/common/wm/dock/docked_window_layout_manager.cc
+++ b/ash/common/wm/dock/docked_window_layout_manager.cc
@@ -25,6 +25,7 @@
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_macros.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura/window.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
@@ -181,11 +182,11 @@
 
 void UndockWindow(WmWindow* window) {
   gfx::Rect previous_bounds = window->GetBounds();
-  WmWindow* old_parent = window->GetParent();
+  aura::Window* old_parent = window->aura_window()->parent();
   window->SetParentUsingContext(window, gfx::Rect());
-  if (window->GetParent() != old_parent) {
-    wm::ReparentTransientChildrenOfChild(window, old_parent,
-                                         window->GetParent());
+  if (window->aura_window()->parent() != old_parent) {
+    wm::ReparentTransientChildrenOfChild(window->aura_window(), old_parent,
+                                         window->aura_window()->parent());
   }
   // Start maximize or fullscreen (affecting packaged apps) animation from
   // previous window bounds.
diff --git a/ash/common/wm/dock/docked_window_resizer.cc b/ash/common/wm/dock/docked_window_resizer.cc
index f2ea238..e50ecd0 100644
--- a/ash/common/wm/dock/docked_window_resizer.cc
+++ b/ash/common/wm/dock/docked_window_resizer.cc
@@ -188,8 +188,9 @@
     WmWindow* docked_container =
         GetTarget()->GetRootWindow()->GetChildByShellWindowId(
             kShellWindowId_DockedContainer);
-    wm::ReparentChildWithTransientChildren(
-        GetTarget(), GetTarget()->GetParent(), docked_container);
+    wm::ReparentChildWithTransientChildren(GetTarget()->aura_window(),
+                                           GetTarget()->aura_window()->parent(),
+                                           docked_container->aura_window());
     if (!resizer)
       return;
   }
@@ -266,8 +267,9 @@
   if ((is_resized || !is_attached_panel) &&
       is_docked_ != (window->GetParent() == dock_container)) {
     if (is_docked_) {
-      wm::ReparentChildWithTransientChildren(window, window->GetParent(),
-                                             dock_container);
+      wm::ReparentChildWithTransientChildren(window->aura_window(),
+                                             window->aura_window()->parent(),
+                                             dock_container->aura_window());
       action = DOCKED_ACTION_DOCK;
     } else if (window->GetParent()->GetShellWindowId() ==
                kShellWindowId_DockedContainer) {
@@ -279,11 +281,12 @@
       // mouse is).
       gfx::Rect near_last_location(last_location_, gfx::Size());
       // Reparenting will cause Relayout and possible dock shrinking.
-      WmWindow* previous_parent = window->GetParent();
+      aura::Window* previous_parent = window->aura_window()->parent();
       window->SetParentUsingContext(window, near_last_location);
-      if (window->GetParent() != previous_parent) {
-        wm::ReparentTransientChildrenOfChild(window, previous_parent,
-                                             window->GetParent());
+      if (window->aura_window()->parent() != previous_parent) {
+        wm::ReparentTransientChildrenOfChild(window->aura_window(),
+                                             previous_parent,
+                                             window->aura_window()->parent());
       }
       action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE;
     }
diff --git a/ash/common/wm/panels/panel_layout_manager.cc b/ash/common/wm/panels/panel_layout_manager.cc
index 66ddd3ce..ba2b0f8 100644
--- a/ash/common/wm/panels/panel_layout_manager.cc
+++ b/ash/common/wm/panels/panel_layout_manager.cc
@@ -348,10 +348,11 @@
     // back to appropriate container and ignore it.
     // TODO(varkha): Updating bounds during a drag can cause problems and a more
     // general solution is needed. See http://crbug.com/251813 .
-    WmWindow* old_parent = child->GetParent();
+    aura::Window* old_parent = child->aura_window()->parent();
     child->SetParentUsingContext(child,
                                  child->GetRootWindow()->GetBoundsInScreen());
-    wm::ReparentTransientChildrenOfChild(child, old_parent, child->GetParent());
+    wm::ReparentTransientChildrenOfChild(child->aura_window(), old_parent,
+                                         child->aura_window()->parent());
     DCHECK(child->GetParent()->GetShellWindowId() !=
            kShellWindowId_PanelContainer);
     return;
diff --git a/ash/common/wm/panels/panel_window_resizer.cc b/ash/common/wm/panels/panel_window_resizer.cc
index e4047eb8..db2ad880 100644
--- a/ash/common/wm/panels/panel_window_resizer.cc
+++ b/ash/common/wm/panels/panel_window_resizer.cc
@@ -161,11 +161,11 @@
     // We use root window coordinates to ensure that during the drag the panel
     // is reparented to a container in the root window that has that window.
     WmWindow* target_root = target->GetRootWindow();
-    WmWindow* old_parent = target->GetParent();
+    aura::Window* old_parent = target->aura_window()->parent();
     target->SetParentUsingContext(target_root,
                                   target_root->GetBoundsInScreen());
-    wm::ReparentTransientChildrenOfChild(target, old_parent,
-                                         target->GetParent());
+    wm::ReparentTransientChildrenOfChild(target->aura_window(), old_parent,
+                                         target->aura_window()->parent());
   }
 }
 
@@ -180,11 +180,11 @@
     // is reparented to a container in the root window that has that location.
     WmWindow* target = GetTarget();
     WmWindow* target_root = target->GetRootWindow();
-    WmWindow* old_parent = target->GetParent();
+    aura::Window* old_parent = target->aura_window()->parent();
     target->SetParentUsingContext(target_root,
-                                  gfx::Rect(last_location_, gfx::Size()));
-    wm::ReparentTransientChildrenOfChild(target, old_parent,
-                                         target->GetParent());
+                                  target_root->GetBoundsInScreen());
+    wm::ReparentTransientChildrenOfChild(target->aura_window(), old_parent,
+                                         target->aura_window()->parent());
   }
 
   // If we started the drag in one root window and moved into another root
diff --git a/ash/common/wm/window_parenting_utils.cc b/ash/common/wm/window_parenting_utils.cc
index 8a868a22..7f035b8 100644
--- a/ash/common/wm/window_parenting_utils.cc
+++ b/ash/common/wm/window_parenting_utils.cc
@@ -4,23 +4,26 @@
 
 #include "ash/common/wm/window_parenting_utils.h"
 
-#include "ash/common/wm_window.h"
+#include "ui/aura/window.h"
+#include "ui/wm/core/window_util.h"
+
+using aura::Window;
 
 namespace ash {
 namespace wm {
 
-void ReparentChildWithTransientChildren(WmWindow* child,
-                                        WmWindow* old_parent,
-                                        WmWindow* new_parent) {
-  if (child->GetParent() == old_parent)
+void ReparentChildWithTransientChildren(Window* child,
+                                        Window* old_parent,
+                                        Window* new_parent) {
+  if (child->parent() == old_parent)
     new_parent->AddChild(child);
   ReparentTransientChildrenOfChild(child, old_parent, new_parent);
 }
 
-void ReparentTransientChildrenOfChild(WmWindow* child,
-                                      WmWindow* old_parent,
-                                      WmWindow* new_parent) {
-  for (WmWindow* transient_child : child->GetTransientChildren())
+void ReparentTransientChildrenOfChild(Window* child,
+                                      Window* old_parent,
+                                      Window* new_parent) {
+  for (Window* transient_child : ::wm::GetTransientChildren(child))
     ReparentChildWithTransientChildren(transient_child, old_parent, new_parent);
 }
 
diff --git a/ash/common/wm/window_parenting_utils.h b/ash/common/wm/window_parenting_utils.h
index 864e0aa1..0162d39 100644
--- a/ash/common/wm/window_parenting_utils.h
+++ b/ash/common/wm/window_parenting_utils.h
@@ -5,24 +5,26 @@
 #ifndef ASH_COMMON_WM_WINDOW_PARENTING_UTILS_H_
 #define ASH_COMMON_WM_WINDOW_PARENTING_UTILS_H_
 
-namespace ash {
+namespace aura {
+class Window;
+}
 
-class WmWindow;
+namespace ash {
 
 namespace wm {
 
 // Changes the parent of a |child| and all its transient children that are
 // themselves children of |old_parent| to |new_parent|.
-void ReparentChildWithTransientChildren(WmWindow* child,
-                                        WmWindow* old_parent,
-                                        WmWindow* new_parent);
+void ReparentChildWithTransientChildren(aura::Window* child,
+                                        aura::Window* old_parent,
+                                        aura::Window* new_parent);
 
 // Changes the parent of all transient children of a |child| to |new_parent|.
 // Does not change parent of the transient children that are not themselves
 // children of |old_parent|.
-void ReparentTransientChildrenOfChild(WmWindow* child,
-                                      WmWindow* old_parent,
-                                      WmWindow* new_parent);
+void ReparentTransientChildrenOfChild(aura::Window* child,
+                                      aura::Window* old_parent,
+                                      aura::Window* new_parent);
 
 }  // namespace wm
 }  // namespace ash
diff --git a/ash/focus_cycler_unittest.cc b/ash/focus_cycler_unittest.cc
index 0780939..1c42b81 100644
--- a/ash/focus_cycler_unittest.cc
+++ b/ash/focus_cycler_unittest.cc
@@ -13,7 +13,6 @@
 #include "ash/common/system/tray/system_tray.h"
 #include "ash/common/wm_shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/test/status_area_widget_test_helper.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
@@ -79,12 +78,8 @@
   }
 
  protected:
-  // Setup the system tray using StatusAreaWidgetTestHelper and focus_cycler.
+  // Setup the system tray focus cycler.
   void SetUpTrayFocusCycle() {
-    StatusAreaWidget* widget =
-        StatusAreaWidgetTestHelper::GetStatusAreaWidget();
-    widget->CreateTrayViews();
-    widget->Show();
     views::Widget* system_tray_widget = GetPrimarySystemTray()->GetWidget();
     ASSERT_TRUE(system_tray_widget);
     focus_cycler_->AddWidget(system_tray_widget);
diff --git a/ash/sticky_keys/sticky_keys_controller.cc b/ash/sticky_keys/sticky_keys_controller.cc
index abbcaf4..d3257a8a 100644
--- a/ash/sticky_keys/sticky_keys_controller.cc
+++ b/ash/sticky_keys/sticky_keys_controller.cc
@@ -25,18 +25,20 @@
 }
 
 // Handle the common tail of event rewriting.
-ui::EventRewriteStatus RewriteUpdate(bool consumed,
-                                     bool released,
-                                     int mod_down_flags,
-                                     int* flags) {
-  int changed_down_flags = mod_down_flags & ~*flags;
-  *flags |= mod_down_flags;
+ui::EventRewriteStatus RewriteUpdate(
+    bool consumed,
+    bool released,
+    int mod_down_flags,
+    const ui::Event& event,
+    std::unique_ptr<ui::Event>* rewritten_event) {
   if (consumed)
     return ui::EVENT_REWRITE_DISCARD;
-  if (released)
-    return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
-  if (changed_down_flags)
-    return ui::EVENT_REWRITE_REWRITTEN;
+  if (mod_down_flags & ~event.flags()) {
+    *rewritten_event = ui::Event::Clone(event);
+    (*rewritten_event)->set_flags(event.flags() | mod_down_flags);
+    return released ? ui::EVENT_REWRITE_DISPATCH_ANOTHER
+                    : ui::EVENT_REWRITE_REWRITTEN;
+  }
   return ui::EVENT_REWRITE_CONTINUE;
 }
 
@@ -82,84 +84,32 @@
   }
 }
 
-bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
-                                          ui::KeyboardCode key_code,
-                                          int* mod_down_flags,
-                                          bool* released) {
-  return shift_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                           released) ||
-         alt_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                         released) ||
-         altgr_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                           released) ||
-         ctrl_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                          released) ||
-         mod3_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                          released) ||
-         search_sticky_key_->HandleKeyEvent(event, key_code, mod_down_flags,
-                                            released);
+StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
+  return overlay_.get();
 }
 
-bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
-                                            int* mod_down_flags,
-                                            bool* released) {
-  return shift_sticky_key_->HandleMouseEvent(event, mod_down_flags, released) ||
-         alt_sticky_key_->HandleMouseEvent(event, mod_down_flags, released) ||
-         altgr_sticky_key_->HandleMouseEvent(event, mod_down_flags, released) ||
-         ctrl_sticky_key_->HandleMouseEvent(event, mod_down_flags, released) ||
-         mod3_sticky_key_->HandleMouseEvent(event, mod_down_flags, released);
-}
-
-bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
-                                             int* mod_down_flags,
-                                             bool* released) {
-  return shift_sticky_key_->HandleScrollEvent(event, mod_down_flags,
-                                              released) ||
-         alt_sticky_key_->HandleScrollEvent(event, mod_down_flags, released) ||
-         altgr_sticky_key_->HandleScrollEvent(event, mod_down_flags,
-                                              released) ||
-         ctrl_sticky_key_->HandleScrollEvent(event, mod_down_flags, released) ||
-         mod3_sticky_key_->HandleScrollEvent(event, mod_down_flags, released);
-}
-
-ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
-    const ui::KeyEvent& event,
-    ui::KeyboardCode key_code,
-    int* flags) {
+ui::EventRewriteStatus StickyKeysController::RewriteEvent(
+    const ui::Event& event,
+    std::unique_ptr<ui::Event>* rewritten_event) {
+  DCHECK(rewritten_event);
   if (!enabled_)
     return ui::EVENT_REWRITE_CONTINUE;
-  int mod_down_flags = 0;
-  bool released = false;
-  bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
-  UpdateOverlay();
-  return RewriteUpdate(consumed, released, mod_down_flags, flags);
-}
 
-ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
-    const ui::MouseEvent& event,
-    int* flags) {
-  if (!enabled_)
-    return ui::EVENT_REWRITE_CONTINUE;
-  int mod_down_flags = 0;
-  bool released = false;
-  bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
-  UpdateOverlay();
-  return RewriteUpdate(consumed, released, mod_down_flags, flags);
-}
+  if (event.IsKeyEvent())
+    return RewriteKeyEvent(*event.AsKeyEvent(), rewritten_event);
 
-ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
-    const ui::ScrollEvent& event,
-    int* flags) {
-  if (!enabled_)
-    return ui::EVENT_REWRITE_CONTINUE;
-  int mod_down_flags = 0;
-  bool released = false;
-  bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
-  UpdateOverlay();
-  return RewriteUpdate(consumed, released, mod_down_flags, flags);
+  if (event.IsMouseEvent())
+    return RewriteMouseEvent(*event.AsMouseEvent(), rewritten_event);
+
+  if (event.IsScrollEvent())
+    return RewriteScrollEvent(*event.AsScrollEvent(), rewritten_event);
+
+  NOTREACHED();
+  return ui::EVENT_REWRITE_CONTINUE;
 }
 
 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
+    const ui::Event& last_event,
     std::unique_ptr<ui::Event>* new_event) {
   DCHECK(new_event);
   new_event->reset();
@@ -176,6 +126,56 @@
   return ui::EVENT_REWRITE_REWRITTEN;
 }
 
+ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
+    const ui::KeyEvent& event,
+    std::unique_ptr<ui::Event>* rewritten_event) {
+  DCHECK(enabled_);
+  int mod_down_flags = 0;
+  bool released = false;
+  bool consumed =
+      shift_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released) ||
+      alt_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released) ||
+      altgr_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released) ||
+      ctrl_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released) ||
+      mod3_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released) ||
+      search_sticky_key_->HandleKeyEvent(event, &mod_down_flags, &released);
+  UpdateOverlay();
+  return RewriteUpdate(consumed, released, mod_down_flags, event,
+                       rewritten_event);
+}
+
+ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
+    const ui::MouseEvent& event,
+    std::unique_ptr<ui::Event>* rewritten_event) {
+  DCHECK(enabled_);
+  int mod_down_flags = 0;
+  bool released = false;
+  bool consumed =
+      shift_sticky_key_->HandleMouseEvent(event, &mod_down_flags, &released) ||
+      alt_sticky_key_->HandleMouseEvent(event, &mod_down_flags, &released) ||
+      altgr_sticky_key_->HandleMouseEvent(event, &mod_down_flags, &released) ||
+      ctrl_sticky_key_->HandleMouseEvent(event, &mod_down_flags, &released) ||
+      mod3_sticky_key_->HandleMouseEvent(event, &mod_down_flags, &released);
+  return RewriteUpdate(consumed, released, mod_down_flags, event,
+                       rewritten_event);
+}
+
+ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
+    const ui::ScrollEvent& event,
+    std::unique_ptr<ui::Event>* rewritten_event) {
+  DCHECK(enabled_);
+  int mod_down_flags = 0;
+  bool released = false;
+  bool consumed =
+      shift_sticky_key_->HandleScrollEvent(event, &mod_down_flags, &released) ||
+      alt_sticky_key_->HandleScrollEvent(event, &mod_down_flags, &released) ||
+      altgr_sticky_key_->HandleScrollEvent(event, &mod_down_flags, &released) ||
+      ctrl_sticky_key_->HandleScrollEvent(event, &mod_down_flags, &released) ||
+      mod3_sticky_key_->HandleScrollEvent(event, &mod_down_flags, &released);
+  return RewriteUpdate(consumed, released, mod_down_flags, event,
+                       rewritten_event);
+}
+
 void StickyKeysController::UpdateOverlay() {
   overlay_->SetModifierKeyState(ui::EF_SHIFT_DOWN,
                                 shift_sticky_key_->current_state());
@@ -201,10 +201,6 @@
   overlay_->Show(enabled_ && key_in_use);
 }
 
-StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
-  return overlay_.get();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 //  StickyKeysHandler
 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
@@ -216,16 +212,15 @@
 StickyKeysHandler::~StickyKeysHandler() {}
 
 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
-                                       ui::KeyboardCode key_code,
                                        int* mod_down_flags,
                                        bool* released) {
   switch (current_state_) {
     case STICKY_KEY_STATE_DISABLED:
-      return HandleDisabledState(event, key_code);
+      return HandleDisabledState(event);
     case STICKY_KEY_STATE_ENABLED:
-      return HandleEnabledState(event, key_code, mod_down_flags, released);
+      return HandleEnabledState(event, mod_down_flags, released);
     case STICKY_KEY_STATE_LOCKED:
-      return HandleLockedState(event, key_code, mod_down_flags, released);
+      return HandleLockedState(event, mod_down_flags, released);
   }
   NOTREACHED();
   return false;
@@ -334,9 +329,8 @@
   return type == ui::ET_KEY_PRESSED ? OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
 }
 
-bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
-                                            ui::KeyboardCode key_code) {
-  switch (TranslateKeyEvent(event.type(), key_code)) {
+bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event) {
+  switch (TranslateKeyEvent(event.type(), event.key_code())) {
     case TARGET_MODIFIER_UP:
       if (preparing_to_enable_) {
         preparing_to_enable_ = false;
@@ -362,10 +356,9 @@
 }
 
 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event,
-                                           ui::KeyboardCode key_code,
                                            int* mod_down_flags,
                                            bool* released) {
-  switch (TranslateKeyEvent(event.type(), key_code)) {
+  switch (TranslateKeyEvent(event.type(), event.key_code())) {
     case NORMAL_KEY_UP:
     case TARGET_MODIFIER_DOWN:
       return false;
@@ -388,10 +381,9 @@
 }
 
 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
-                                          ui::KeyboardCode key_code,
                                           int* mod_down_flags,
                                           bool* released) {
-  switch (TranslateKeyEvent(event.type(), key_code)) {
+  switch (TranslateKeyEvent(event.type(), event.key_code())) {
     case TARGET_MODIFIER_DOWN:
       return true;
     case TARGET_MODIFIER_UP:
diff --git a/ash/sticky_keys/sticky_keys_controller.h b/ash/sticky_keys/sticky_keys_controller.h
index 327d7ec..cdfc2de 100644
--- a/ash/sticky_keys/sticky_keys_controller.h
+++ b/ash/sticky_keys/sticky_keys_controller.h
@@ -59,10 +59,10 @@
 // modifiers. Each handling or state is performed independently.
 //
 // StickyKeysController is disabled by default.
-class ASH_EXPORT StickyKeysController {
+class ASH_EXPORT StickyKeysController : public ui::EventRewriter {
  public:
   StickyKeysController();
-  virtual ~StickyKeysController();
+  ~StickyKeysController() override;
 
   // Activate sticky keys to intercept and modify incoming events.
   void Enable(bool enabled);
@@ -73,61 +73,29 @@
   // passed.
   StickyKeysOverlay* GetOverlayForTest();
 
-  // Handles keyboard event. Returns an |EventRewriteStatus|, and may
-  // modify |flags|:
-  // - Returns ui::EVENT_REWRITE_DISCARD, and leaves |flags| untouched,
-  //   if the event is consumed (i.e. a sticky modifier press or release);
-  // - Returns ui::EVENT_REWRITE_REWRITTEN if the event needs to be modified
-  //   according to the returned |flags| (i.e. a sticky-modified key);
-  // - Returns ui::EVENT_REWRITE_DISPATCH_ANOTHER if the event needs to be
-  //   modified according to the returned |flags|, and there are delayed
-  //   modifier-up   events now to be retrieved using |NextDispatchEvent()|
-  //   (i.e. a sticky-modified key that ends a sticky state);
-  // - Otherwise returns ui::EVENT_REWRITE_CONTINUE and leaves |flags|
-  //   unchanged.
-  ui::EventRewriteStatus RewriteKeyEvent(const ui::KeyEvent& event,
-                                         ui::KeyboardCode key_code,
-                                         int* flags);
-
-  // Handles mouse event.
-  ui::EventRewriteStatus RewriteMouseEvent(const ui::MouseEvent& event,
-                                           int* flags);
-
-  // Handles scroll event.
-  ui::EventRewriteStatus RewriteScrollEvent(const ui::ScrollEvent& event,
-                                            int* flags);
-
-  // Obtains a pending modifier-up event. If the immediately previous
-  // call to |Rewrite...Event()| or |NextDispatchEvent()| returned
-  // ui::EVENT_REWRITE_DISPATCH_ANOTHER, this sets |new_event| and returns:
-  // - ui::EVENT_REWRITE_DISPATCH_ANOTHER if there is at least one more
-  //   pending modifier-up event;
-  // - ui::EVENT_REWRITE_REWRITE if this is the last or only modifier-up event;
-  // Otherwise, there is no pending modifier-up event, and this function
-  // returns ui::EVENT_REWRITE_CONTINUE and sets |new_event| to NULL.
+  // ui::EventRewriter:
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* rewritten_event) override;
   ui::EventRewriteStatus NextDispatchEvent(
-      std::unique_ptr<ui::Event>* new_event);
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override;
 
  private:
-  // Handles keyboard event. Returns true if Sticky key consumes keyboard event.
-  // Adds to |mod_down_flags| any flag to be added to the key event.
-  // Sets |released| if any modifier is to be released after the key event.
-  bool HandleKeyEvent(const ui::KeyEvent& event,
-                      ui::KeyboardCode key_code,
-                      int* mod_down_flags,
-                      bool* released);
+  // Rewrite keyboard event.
+  ui::EventRewriteStatus RewriteKeyEvent(
+      const ui::KeyEvent& event,
+      std::unique_ptr<ui::Event>* rewritten_event);
 
-  // Handles mouse event. Returns true if Sticky key consumes keyboard event.
-  // Sets |released| if any modifier is to be released after the key event.
-  bool HandleMouseEvent(const ui::MouseEvent& event,
-                        int* mod_down_flags,
-                        bool* released);
+  // Rewrite mouse event.
+  ui::EventRewriteStatus RewriteMouseEvent(
+      const ui::MouseEvent& event,
+      std::unique_ptr<ui::Event>* rewritten_event);
 
-  // Handles scroll event. Returns true if Sticky key consumes keyboard event.
-  // Sets |released| if any modifier is to be released after the key event.
-  bool HandleScrollEvent(const ui::ScrollEvent& event,
-                         int* mod_down_flags,
-                         bool* released);
+  // Rewrite scroll event.
+  ui::EventRewriteStatus RewriteScrollEvent(
+      const ui::ScrollEvent& event,
+      std::unique_ptr<ui::Event>* rewritten_event);
 
   // Updates the overlay UI with the current state of the sticky keys.
   void UpdateOverlay();
@@ -192,7 +160,6 @@
   // Sets its own modifier flag in |mod_down_flags| if it is active and needs
   // to be added to the event, and sets |released| if releasing it.
   bool HandleKeyEvent(const ui::KeyEvent& event,
-                      ui::KeyboardCode key_code,
                       int* mod_down_flags,
                       bool* released);
 
@@ -234,20 +201,17 @@
 
   // Handles key event in DISABLED state. Returns true if sticky keys
   // consumes the keyboard event.
-  bool HandleDisabledState(const ui::KeyEvent& event,
-                           ui::KeyboardCode key_code);
+  bool HandleDisabledState(const ui::KeyEvent& event);
 
   // Handles key event in ENABLED state. Returns true if sticky keys
   // consumes the keyboard event.
   bool HandleEnabledState(const ui::KeyEvent& event,
-                          ui::KeyboardCode key_code,
                           int* mod_down_flags,
                           bool* released);
 
   // Handles key event in LOCKED state. Returns true if sticky keys
   // consumes the keyboard event.
   bool HandleLockedState(const ui::KeyEvent& event,
-                         ui::KeyboardCode key_code,
                          int* mod_down_flags,
                          bool* released);
 
diff --git a/ash/sticky_keys/sticky_keys_unittest.cc b/ash/sticky_keys/sticky_keys_unittest.cc
index 457e3633..c1f32a9 100644
--- a/ash/sticky_keys/sticky_keys_unittest.cc
+++ b/ash/sticky_keys/sticky_keys_unittest.cc
@@ -205,23 +205,23 @@
     int down_flags = 0;
     std::unique_ptr<ui::KeyEvent> ev;
     ev.reset(GenerateKey(ui::ET_KEY_PRESSED, key_code));
-    handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
+    handler->HandleKeyEvent(*ev.get(), &down_flags, &released);
     ev.reset(GenerateKey(ui::ET_KEY_RELEASED, key_code));
-    handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
+    handler->HandleKeyEvent(*ev.get(), &down_flags, &released);
   }
 
   bool HandleKeyEvent(const ui::KeyEvent& key_event,
                       StickyKeysHandler* handler,
                       int* down,
                       bool* up) {
-    return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up);
+    return handler->HandleKeyEvent(key_event, down, up);
   }
 
   int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event,
                                  StickyKeysHandler* handler) {
     bool released = false;
     int down = 0;
-    handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released);
+    handler->HandleKeyEvent(key_event, &down, &released);
     return down;
   }
 
diff --git a/base/process/process_linux.cc b/base/process/process_linux.cc
index bcba9b5..f98bb4c0 100644
--- a/base/process/process_linux.cc
+++ b/base/process/process_linux.cc
@@ -60,10 +60,12 @@
         foreground_type == FILE_SYSTEM_CGROUP &&
         background_type == FILE_SYSTEM_CGROUP;
   }
-};
 
-base::LazyInstance<CGroups>::DestructorAtExit g_cgroups =
-    LAZY_INSTANCE_INITIALIZER;
+  static CGroups& Get() {
+    static auto& groups = *new CGroups;
+    return groups;
+  }
+};
 #else
 const int kBackgroundPriority = 5;
 #endif  // defined(OS_CHROMEOS)
@@ -87,7 +89,7 @@
 // static
 bool Process::CanBackgroundProcesses() {
 #if defined(OS_CHROMEOS)
-  if (g_cgroups.Get().enabled)
+  if (CGroups::Get().enabled)
     return true;
 #endif  // defined(OS_CHROMEOS)
 
@@ -100,7 +102,7 @@
   DCHECK(IsValid());
 
 #if defined(OS_CHROMEOS)
-  if (g_cgroups.Get().enabled) {
+  if (CGroups::Get().enabled) {
     // Used to allow reading the process priority from proc on thread launch.
     base::ThreadRestrictions::ScopedAllowIO allow_io;
     std::string proc;
@@ -119,11 +121,10 @@
   DCHECK(IsValid());
 
 #if defined(OS_CHROMEOS)
-  if (g_cgroups.Get().enabled) {
+  if (CGroups::Get().enabled) {
     std::string pid = IntToString(process_);
-    const base::FilePath file =
-        background ?
-            g_cgroups.Get().background_file : g_cgroups.Get().foreground_file;
+    const base::FilePath file = background ? CGroups::Get().background_file
+                                           : CGroups::Get().foreground_file;
     return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
   }
 #endif  // defined(OS_CHROMEOS)
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index b2340ca..1c4a663 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -97,14 +97,21 @@
 
 // Set of live launch test processes with corresponding lock (it is allowed
 // for callers to launch processes on different threads).
-LazyInstance<std::map<ProcessHandle, CommandLine>>::DestructorAtExit
-    g_live_processes = LAZY_INSTANCE_INITIALIZER;
-LazyInstance<Lock>::DestructorAtExit g_live_processes_lock =
-    LAZY_INSTANCE_INITIALIZER;
+Lock* GetLiveProcessesLock() {
+  static auto* lock = new Lock;
+  return lock;
+}
+
+std::map<ProcessHandle, CommandLine>* GetLiveProcesses() {
+  static auto* map = new std::map<ProcessHandle, CommandLine>;
+  return map;
+}
 
 // Performance trace generator.
-LazyInstance<TestLauncherTracer>::DestructorAtExit g_tracer =
-    LAZY_INSTANCE_INITIALIZER;
+TestLauncherTracer* GetTestLauncherTracer() {
+  static auto* tracer = new TestLauncherTracer;
+  return tracer;
+}
 
 #if defined(OS_POSIX)
 // Self-pipe that makes it possible to do complex shutdown handling
@@ -118,19 +125,15 @@
 void KillSpawnedTestProcesses() {
   // Keep the lock until exiting the process to prevent further processes
   // from being spawned.
-  AutoLock lock(g_live_processes_lock.Get());
+  AutoLock lock(*GetLiveProcessesLock());
 
-  fprintf(stdout,
-          "Sending SIGTERM to %" PRIuS " child processes... ",
-          g_live_processes.Get().size());
+  fprintf(stdout, "Sending SIGTERM to %" PRIuS " child processes... ",
+          GetLiveProcesses()->size());
   fflush(stdout);
 
-  for (std::map<ProcessHandle, CommandLine>::iterator i =
-           g_live_processes.Get().begin();
-       i != g_live_processes.Get().end();
-       ++i) {
+  for (const auto& pair : *GetLiveProcesses()) {
     // Send the signal to entire process group.
-    kill((-1) * (i->first), SIGTERM);
+    kill((-1) * (pair.first), SIGTERM);
   }
 
   fprintf(stdout,
@@ -142,17 +145,13 @@
   fprintf(stdout, "done.\n");
   fflush(stdout);
 
-  fprintf(stdout,
-          "Sending SIGKILL to %" PRIuS " child processes... ",
-          g_live_processes.Get().size());
+  fprintf(stdout, "Sending SIGKILL to %" PRIuS " child processes... ",
+          GetLiveProcesses()->size());
   fflush(stdout);
 
-  for (std::map<ProcessHandle, CommandLine>::iterator i =
-           g_live_processes.Get().begin();
-       i != g_live_processes.Get().end();
-       ++i) {
+  for (const auto& pair : *GetLiveProcesses()) {
     // Send the signal to entire process group.
-    kill((-1) * (i->first), SIGKILL);
+    kill((-1) * (pair.first), SIGKILL);
   }
 
   fprintf(stdout, "done.\n");
@@ -309,11 +308,11 @@
     // Note how we grab the lock before the process possibly gets created.
     // This ensures that when the lock is held, ALL the processes are registered
     // in the set.
-    AutoLock lock(g_live_processes_lock.Get());
+    AutoLock lock(*GetLiveProcessesLock());
 
 #if defined(OS_WIN)
     // Allow the handle used to capture stdio and stdout to be inherited by the
-    // child. Note that this is done under g_live_processes_lock to ensure that
+    // child. Note that this is done under GetLiveProcessesLock() to ensure that
     // only the desired child receives the handle.
     if (new_options.stdout_handle) {
       ::SetHandleInformation(new_options.stdout_handle, HANDLE_FLAG_INHERIT,
@@ -325,7 +324,7 @@
 
 #if defined(OS_WIN)
     // Revoke inheritance so that the handle isn't leaked into other children.
-    // Note that this is done under g_live_processes_lock to ensure that only
+    // Note that this is done under GetLiveProcessesLock() to ensure that only
     // the desired child receives the handle.
     if (new_options.stdout_handle)
       ::SetHandleInformation(new_options.stdout_handle, HANDLE_FLAG_INHERIT, 0);
@@ -335,8 +334,7 @@
       return -1;
 
     // TODO(rvargas) crbug.com/417532: Don't store process handles.
-    g_live_processes.Get().insert(std::make_pair(process.Handle(),
-                                                 command_line));
+    GetLiveProcesses()->insert(std::make_pair(process.Handle(), command_line));
   }
 
   if (!launched_callback.is_null())
@@ -355,7 +353,7 @@
     // Note how we grab the log before issuing a possibly broad process kill.
     // Other code parts that grab the log kill processes, so avoid trying
     // to do that twice and trigger all kinds of log messages.
-    AutoLock lock(g_live_processes_lock.Get());
+    AutoLock lock(*GetLiveProcessesLock());
 
 #if defined(OS_POSIX)
     if (exit_code != 0) {
@@ -367,11 +365,11 @@
     }
 #endif
 
-    g_live_processes.Get().erase(process.Handle());
+    GetLiveProcesses()->erase(process.Handle());
   }
 
-  g_tracer.Get().RecordProcessExecution(start_time,
-                                        TimeTicks::Now() - start_time);
+  GetTestLauncherTracer()->RecordProcessExecution(
+      start_time, TimeTicks::Now() - start_time);
 
   return exit_code;
 }
@@ -1114,7 +1112,7 @@
   if (command_line->HasSwitch(switches::kTestLauncherTrace)) {
     FilePath trace_path(
         command_line->GetSwitchValuePath(switches::kTestLauncherTrace));
-    if (!g_tracer.Get().Dump(trace_path)) {
+    if (!GetTestLauncherTracer()->Dump(trace_path)) {
       LOG(ERROR) << "Failed to save test launcher trace.";
     }
   }
@@ -1159,18 +1157,15 @@
 void TestLauncher::OnOutputTimeout() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  AutoLock lock(g_live_processes_lock.Get());
+  AutoLock lock(*GetLiveProcessesLock());
 
   fprintf(stdout, "Still waiting for the following processes to finish:\n");
 
-  for (std::map<ProcessHandle, CommandLine>::iterator i =
-           g_live_processes.Get().begin();
-       i != g_live_processes.Get().end();
-       ++i) {
+  for (const auto& pair : *GetLiveProcesses()) {
 #if defined(OS_WIN)
-    fwprintf(stdout, L"\t%s\n", i->second.GetCommandLineString().c_str());
+    fwprintf(stdout, L"\t%s\n", pair.second.GetCommandLineString().c_str());
 #else
-    fprintf(stdout, "\t%s\n", i->second.GetCommandLineString().c_str());
+    fprintf(stdout, "\t%s\n", pair.second.GetCommandLineString().c_str());
 #endif
   }
 
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 52a432b8..3565b8b 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -216,12 +216,18 @@
   total_virtual_size = stats.size_allocated;
   allocated_objects_size = stats.size_in_use;
 
-  // The resident size is approximated to the max size in use, which would count
-  // the total size of all regions other than the free bytes at the end of each
-  // region. In each allocation region the allocations are rounded off to a
-  // fixed quantum, so the excess region will not be resident.
-  // See crrev.com/1531463004 for detailed explanation.
-  resident_size = stats.max_size_in_use;
+  // Resident size is approximated pretty well by stats.max_size_in_use.
+  // However, on macOS, freed blocks are both resident and reusable, which is
+  // semantically equivalent to deallocated. The implementation of libmalloc
+  // will also only hold a fixed number of freed regions before actually
+  // starting to deallocate them, so stats.max_size_in_use is also not
+  // representative of the peak size. As a result, stats.max_size_in_use is
+  // typically somewhere between actually resident [non-reusable] pages, and
+  // peak size. This is not very useful, so we just use stats.size_in_use for
+  // resident_size, even though it's an underestimate and fails to account for
+  // fragmentation. See
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
+  resident_size = stats.size_in_use;
 #elif defined(OS_WIN)
   WinHeapInfo main_heap_info = {};
   WinHeapMemoryDumpImpl(&main_heap_info);
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index f497d34..cc0e2dd 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -325,7 +325,8 @@
             android:exported="false"
             android:windowSoftInputMode="adjustResize"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:hardwareAccelerated="false">
+            android:hardwareAccelerated="false"
+            {{ self.supports_video_persistence() }} >
         </activity>
         <activity android:name="org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity"
             android:theme="@style/MainTheme"
@@ -335,7 +336,8 @@
             android:persistableMode="persistNever"
             android:autoRemoveFromRecents="false"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:hardwareAccelerated="false">
+            android:hardwareAccelerated="false"
+            {{ self.supports_video_persistence() }} >
         </activity>
         {% for i in range(10) %}
         <activity android:name="org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity{{ i }}"
@@ -347,7 +349,8 @@
             android:persistableMode="persistNever"
             android:taskAffinity=""
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:hardwareAccelerated="false">
+            android:hardwareAccelerated="false"
+            {{ self.supports_video_persistence() }} >
         </activity>
         {% endfor %}
 
@@ -359,6 +362,8 @@
             android:launchMode="singleTask"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
             android:hardwareAccelerated="false"
+            {# We can only use blocks once in Jinja, for future substitutions we use
+            self.supports_video_persistence(). #}
             {% block supports_video_persistence %}
             {% endblock %}
             >
@@ -404,7 +409,9 @@
              android:taskAffinity="{{ manifest_package }}.ChromeTabbedActivity2"
              android:launchMode="singleTask"
              android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-             android:hardwareAccelerated="false">
+             android:hardwareAccelerated="false"
+             {{ self.supports_video_persistence() }}
+             >
         </activity>
         <activity android:name="org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity"
             android:theme="@style/TabbedModeTheme"
@@ -525,11 +532,13 @@
             android:windowSoftInputMode="adjustResize"
             android:persistableMode="persistNever"
             android:hardwareAccelerated="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+            {{ self.supports_video_persistence() }} >
         </activity>
         <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappActivity"
             android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity"
-            android:label="@string/webapp_activity_title">
+            android:label="@string/webapp_activity_title"
+            {{ self.supports_video_persistence() }} >
         </activity-alias>
         {% for i in range(10) %}
         <activity android:name="org.chromium.chrome.browser.webapps.WebappActivity{{ i }}"
@@ -541,7 +550,8 @@
             android:persistableMode="persistNever"
             android:hardwareAccelerated="false"
             android:taskAffinity="{{ manifest_package }}.webapps.WebappActivity{{ i }}"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+            {{ self.supports_video_persistence() }} >
         </activity>
         <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappActivity{{ i }}"
             android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity{{ i }}"
@@ -558,7 +568,8 @@
             android:windowSoftInputMode="adjustResize"
             android:persistableMode="persistNever"
             android:hardwareAccelerated="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+            {{ self.supports_video_persistence() }} >
         </activity>
         {% for i in range(10) %}
         <activity android:name="org.chromium.chrome.browser.webapps.WebApkActivity{{ i }}"
@@ -570,7 +581,8 @@
             android:persistableMode="persistNever"
             android:hardwareAccelerated="false"
             android:taskAffinity="{{ manifest_package }}.webapps.WebApkActivity{{ i }}"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+            {{ self.supports_video_persistence() }} >
         </activity>
         {% endfor %}
 
diff --git a/chrome/android/java/res/layout/item_chooser_dialog.xml b/chrome/android/java/res/layout/item_chooser_dialog.xml
index d1aa10d..3806426 100644
--- a/chrome/android/java/res/layout/item_chooser_dialog.xml
+++ b/chrome/android/java/res/layout/item_chooser_dialog.xml
@@ -22,6 +22,17 @@
         android:paddingEnd="16dp"
         android:textSize="16sp" />
 
+    <!-- The "no item found" message. -->
+    <org.chromium.ui.widget.TextViewWithClickableSpans
+        android:id="@+id/not_found_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:textSize="16sp"
+        android:visibility="gone" />
+
     <!-- A layout containing a spinning progress bar that gets replaced with a
          list of items. -->
     <FrameLayout
@@ -29,18 +40,6 @@
         android:layout_width="fill_parent"
         android:layout_height="100dp" >
 
-        <!-- The "no item found" message. -->
-        <org.chromium.ui.widget.TextViewWithClickableSpans
-            android:id="@+id/not_found_message"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_marginTop="8dp"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
-            android:textSize="16sp"
-            android:visibility="gone" />
-
         <ProgressBar
             android:id="@+id/progress"
             android:layout_width="24dp"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index cb0b5ba..e1dc768 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -85,6 +85,7 @@
 import org.chromium.chrome.browser.history.HistoryManagerUtils;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
+import org.chromium.chrome.browser.media.VideoPersister;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.StartupMetrics;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
@@ -812,6 +813,14 @@
         FeatureUtilities.setCustomTabVisible(isCustomTab());
         FeatureUtilities.setIsInMultiWindowMode(
                 MultiWindowUtils.getInstance().isInMultiWindowMode(this));
+
+        VideoPersister.getInstance().stopPersist(this);
+    }
+
+    @Override
+    protected void onUserLeaveHint() {
+        super.onUserLeaveHint();
+        VideoPersister.getInstance().attemptPersist(this);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 8953448..88ddbec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -75,7 +75,6 @@
 import org.chromium.chrome.browser.incognito.IncognitoNotificationManager;
 import org.chromium.chrome.browser.infobar.DataReductionPromoInfoBar;
 import org.chromium.chrome.browser.locale.LocaleManager;
-import org.chromium.chrome.browser.media.VideoPersister;
 import org.chromium.chrome.browser.metrics.ActivityStopMetrics;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.MainIntentBehaviorMetrics;
@@ -512,7 +511,6 @@
             mMergeTabsOnResume = false;
         }
 
-        VideoPersister.getInstance().stopPersist(this);
         // TODO(mthiesse): Move this call into ChromeActivity. crbug.com/697694
         VrShellDelegate.maybeResumeVR(this);
 
@@ -527,12 +525,6 @@
     }
 
     @Override
-    protected void onUserLeaveHint() {
-        VideoPersister.getInstance().attemptPersist(this);
-        super.onUserLeaveHint();
-    }
-
-    @Override
     public void onPauseWithNative() {
         mTabModelSelectorImpl.commitAllTabClosures();
         CookiesFetcher.persistCookies(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchBarControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchBarControl.java
index c332ff3..5946d858 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchBarControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchBarControl.java
@@ -221,15 +221,15 @@
     }
 
     /**
-     * Sets the search context to display in the control.
+     * Sets the details of the context to display in the control.
      * @param selection The portion of the context that represents the user's selection.
      * @param end The portion of the context after the selection.
      */
-    public void setSearchContext(String selection, String end) {
+    public void setContextDetails(String selection, String end) {
         cancelSearchTermResolutionAnimation();
         hideCaption();
         mQuickActionControl.reset();
-        mContextControl.setSearchContext(selection, end);
+        mContextControl.setContextDetails(selection, end);
         resetSearchBarContextOpacity();
         animateDividerLine(false);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchContextControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchContextControl.java
index 542ad59..cc344760 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchContextControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchContextControl.java
@@ -43,11 +43,11 @@
     }
 
     /**
-     * Sets the search context to display in the control.
+     * Sets the details of the search context to display in the control.
      * @param selection The portion of the context that represents the user's selection.
      * @param end The portion of the context after the selection.
      */
-    public void setSearchContext(String selection, String end) {
+    public void setContextDetails(String selection, String end) {
         inflate();
 
         mSelectedText.setText(sanitizeText(selection));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index f5c07f99..baecee1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -546,14 +546,16 @@
     }
 
     /**
-     * Sets the search context to display in the SearchBar.
+     * Sets the search context details to display in the SearchBar.
      * @param selection The portion of the context that represents the user's selection.
      * @param end The portion of the context from the selection to its end.
      */
-    public void setSearchContext(String selection, String end) {
+    public void setContextDetails(String selection, String end) {
         getImageControl().hideStaticImage(true);
-        getSearchBarControl().setSearchContext(selection, end);
+        getSearchBarControl().setContextDetails(selection, end);
         mPanelMetrics.onSearchRequestStarted();
+        // Make sure the new Context draws.
+        requestUpdate();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
new file mode 100644
index 0000000..1ad2a65
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
@@ -0,0 +1,77 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.contextualsearch;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * Provides a context in which to search, and links to the native ContextualSearchContext.
+ * Includes the selection, selection offsets, surrounding page content, etc.
+ */
+public class ContextualSearchContext {
+    // The initial selection that established this context, or null.
+    private final String mSelection;
+
+    // Pointer to the native instance of this class.
+    private long mNativePointer;
+
+    /**
+     * Constructs a context that cannot resolve a search term and has a small amount of
+     * page content.
+     */
+    ContextualSearchContext() {
+        mNativePointer = nativeInit();
+        mSelection = null;
+    }
+
+    /**
+     * Constructs a context that can resolve a search term and has a large amount of
+     * page content.
+     * @param selection The current selection.
+     * @param homeCountry The country where the user usually resides, or an empty string if not
+     *        known.
+     * @param maySendBasePageUrl Whether policy allows sending the base-page URL to the server.
+     */
+    ContextualSearchContext(String selection, String homeCountry, boolean maySendBasePageUrl) {
+        mNativePointer = nativeInit();
+        mSelection = selection;
+        nativeSetResolveProperties(getNativePointer(), selection, homeCountry, maySendBasePageUrl);
+    }
+
+    /**
+     * This method should be called to clean up storage when an instance of this class is
+     * no longer in use.  The nativeDestroy will call the destructor on the native instance.
+     */
+    void destroy() {
+        assert mNativePointer != 0;
+        nativeDestroy(mNativePointer);
+        mNativePointer = 0;
+    }
+
+    /**
+     * @return the original selection.
+     */
+    String getSelection() {
+        return mSelection;
+    }
+
+    // ============================================================================================
+    // Native callback support.
+    // ============================================================================================
+
+    @CalledByNative
+    private long getNativePointer() {
+        assert mNativePointer != 0;
+        return mNativePointer;
+    }
+
+    // ============================================================================================
+    // Native methods.
+    // ============================================================================================
+    private native long nativeInit();
+    private native void nativeDestroy(long nativeContextualSearchContext);
+    private native void nativeSetResolveProperties(long nativeContextualSearchContext,
+            String selection, String homeCountry, boolean maySendBasePageUrl);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 0861373..5287bd6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -120,6 +120,9 @@
     private boolean mWasActivatedByTap;
     private boolean mIsInitialized;
 
+    // The current search context, or null.
+    private ContextualSearchContext mContext;
+
     /**
      * This boolean is used for loading content after a long-press when content is not immediately
      * loaded.
@@ -303,8 +306,9 @@
     /**
      * @return The Base Page's {@link ContentViewCore}.
      */
-    @Nullable private ContentViewCore getBaseContentView() {
-        return mSelectionController.getBaseContentView();
+    @Nullable
+    private WebContents getBaseWebContents() {
+        return mSelectionController.getBaseWebContents();
     }
 
     /**
@@ -326,6 +330,8 @@
      * @param reason The {@link StateChangeReason} for hiding Contextual Search.
      */
     public void hideContextualSearch(StateChangeReason reason) {
+        if (mContext != null) mContext.destroy();
+        mContext = null;
         if (mSearchPanel == null) return;
 
         if (mSearchPanel.isShowing()) {
@@ -419,6 +425,7 @@
         // TODO(pedrosimonetti): Fix for M47. Replace this with a better delayed load approach.
         mSearchPanel.destroyContent();
 
+        String selection = mSelectionController.getSelectedText();
         boolean isTap = mSelectionController.getSelectionType() == SelectionType.TAP;
         boolean didRequestSurroundings = false;
         if (isTap) {
@@ -426,42 +433,42 @@
             mShouldLoadDelayedSearch = false;
         }
         if (isTap && mPolicy.shouldPreviousTapResolve()) {
-            mNetworkCommunicator.startSearchTermResolutionRequest(
-                    mSelectionController.getSelectedText());
+            if (mContext != null) mContext.destroy();
+            mContext = new ContextualSearchContext(
+                    selection, mPolicy.getHomeCountry(mActivity), mPolicy.maySendBasePageUrl());
+            nativeGatherSurroundingText(
+                    mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
             didRequestSurroundings = true;
             // Cache the native translate data, so JNI calls won't be made when time-critical.
             mTranslateController.cacheNativeTranslateData();
         } else {
             boolean shouldPrefetch = mPolicy.shouldPrefetchSearchResult();
-            mSearchRequest = createContextualSearchRequest(
-                    mSelectionController.getSelectedText(), null, null, shouldPrefetch);
+            mSearchRequest = createContextualSearchRequest(selection, null, null, shouldPrefetch);
             mTranslateController.forceAutoDetectTranslateUnlessDisabled(mSearchRequest);
             mDidStartLoadingResolvedSearchRequest = false;
-            mSearchPanel.setSearchTerm(mSelectionController.getSelectedText());
+            mSearchPanel.setSearchTerm(selection);
             if (shouldPrefetch) loadSearchUrl();
 
             // Record metrics for manual refinement of the search term from long-press.
             // TODO(donnd): remove this section once metrics have been analyzed.
-            if (mSelectionController.getSelectionType() == SelectionType.LONG_PRESS
-                    && mSearchPanel.isPeeking()) {
+            if (!isTap && mSearchPanel.isPeeking()) {
                 boolean isSingleWord =
-                        !CONTAINS_WHITESPACE_PATTERN
-                                 .matcher(mSelectionController.getSelectedText().trim())
-                                 .find();
+                        !CONTAINS_WHITESPACE_PATTERN.matcher(selection.trim()).find();
                 RecordUserAction.record(isSingleWord ? "ContextualSearch.ManualRefineSingleWord"
                                                      : "ContextualSearch.ManualRefineMultiWord");
             }
         }
 
-        if (!didRequestSurroundings) {
+        if (!didRequestSurroundings && getBaseWebContents() != null) {
             // Gather surrounding text for Icing integration, which will make the selection and
             // a shorter version of the surroundings available for Conversational Search.
             // Although the surroundings are extracted, they will not be sent to the server as
             // part of search term resolution, just sent to Icing which keeps them local until
             // the user activates a Voice Search.
-            nativeGatherSurroundingText(mNativeContextualSearchManagerPtr,
-                    mSelectionController.getSelectedText(), NO_HOME_COUNTRY,
-                    getBaseContentView().getWebContents(), mPolicy.maySendBasePageUrl());
+            if (mContext != null) mContext.destroy();
+            mContext = new ContextualSearchContext();
+            nativeGatherSurroundingText(
+                    mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
         }
 
         mWereSearchResultsSeen = false;
@@ -492,8 +499,6 @@
             mSearchPanel.setDidSearchInvolvePromo();
         }
 
-        // TODO(donnd): although we are showing the bar here, we have not yet set the text!
-        // Refactor to show the bar and set the text at the same time!
         // TODO(donnd): If there was a previously ongoing contextual search, we should ensure
         // it's registered as closed.
         mSearchPanel.requestPanelShow(stateChangeReason);
@@ -504,21 +509,21 @@
 
     @Override
     public void startSearchTermResolutionRequest(String selection) {
-        ContentViewCore baseContentView = getBaseContentView();
-        if (baseContentView != null) {
-            nativeStartSearchTermResolutionRequest(mNativeContextualSearchManagerPtr, selection,
-                    mPolicy.getHomeCountry(mActivity), getBaseContentView().getWebContents(),
-                    mPolicy.maySendBasePageUrl());
+        WebContents baseWebContents = getBaseWebContents();
+        if (baseWebContents != null) {
+            assert mContext != null;
+            nativeStartSearchTermResolutionRequest(
+                    mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
         }
     }
 
     @Override
     @Nullable public URL getBasePageUrl() {
-        ContentViewCore baseContentViewCore = getBaseContentView();
-        if (baseContentViewCore == null) return null;
+        WebContents baseWebContents = getBaseWebContents();
+        if (baseWebContents == null) return null;
 
         try {
-            return new URL(baseContentViewCore.getWebContents().getUrl());
+            return new URL(baseWebContents.getUrl());
         } catch (MalformedURLException e) {
             return null;
         }
@@ -615,10 +620,11 @@
      */
     @CalledByNative
     private void onSurroundingTextAvailable(final String afterText) {
+        String selection = mSelectionController.getSelectedText();
         // TODO(donnd): check if panel has been requested to show.
         // We used to call mSearchPanel.isShowing() here, but that's unreliable (crbug.com/669600).
-        mSearchPanel.setSearchContext(
-                mSelectionController.getSelectedText(), afterText);
+        mSearchPanel.setContextDetails(selection, afterText);
+        mNetworkCommunicator.startSearchTermResolutionRequest(selection);
     }
 
     /**
@@ -747,8 +753,13 @@
             mPolicy.logSearchTermResolutionDetails(searchTerm);
         }
 
+        // Adjust the selection unless the user changed it since we initiated the search.
         if (selectionStartAdjust != 0 || selectionEndAdjust != 0) {
-            mSelectionController.adjustSelection(selectionStartAdjust, selectionEndAdjust);
+            String originalSelection = mContext == null ? null : mContext.getSelection();
+            if (originalSelection != null
+                    && originalSelection.equals(mSelectionController.getSelectedText())) {
+                mSelectionController.adjustSelection(selectionStartAdjust, selectionEndAdjust);
+            }
         }
     }
 
@@ -1291,16 +1302,21 @@
         if (mIsAccessibilityModeEnabled) return;
 
         if (isTapSupported()) {
-            // Here we are starting a new Contextual Search with a Tap gesture, therefore
-            // we need to clear to properly reflect that a search just started and we don't
-            // have the resolved search term yet.
+            // Here we are probably starting a new Contextual Search with a Tap gesture (or we'll
+            // ignore the tap), therefore we need to clear to properly reflect that a search just
+            // started and we don't have the resolved search term yet.
             mSearchRequest = null;
 
-            // Let the policy know that a tap gesture has been received.
-            mPolicy.registerTap();
+            if (mContext != null) mContext.destroy();
+            mContext = null;
 
-            ContentViewCore baseContentView = getBaseContentView();
-            if (baseContentView != null) baseContentView.getWebContents().selectWordAroundCaret();
+            WebContents baseWebContents = getBaseWebContents();
+            if (baseWebContents != null) {
+                // Let the policy know that a tap gesture has been received.
+                mPolicy.registerTap();
+
+                baseWebContents.selectWordAroundCaret();
+            }
         }
     }
 
@@ -1313,7 +1329,7 @@
             StateChangeReason stateChangeReason = type == SelectionType.TAP
                     ? StateChangeReason.TEXT_SELECT_TAP : StateChangeReason.TEXT_SELECT_LONG_PRESS;
             ContextualSearchUma.logSelectionIsValid(selectionValid);
-            if (selectionValid) {
+            if (selectionValid && mSearchPanel != null) {
                 mSearchPanel.updateBasePageSelectionYPx(y);
                 if (!mSearchPanel.isShowing()) {
                     mSearchPanel.getPanelMetrics().onSelectionEstablished(selection);
@@ -1329,7 +1345,7 @@
     public void handleSelectionDismissal() {
         if (mIsAccessibilityModeEnabled) return;
 
-        if (mSearchPanel.isShowing()
+        if (mSearchPanel != null && mSearchPanel.isShowing()
                 && !mIsPromotingToTab
                 // If the selection is dismissed when the Panel is not peeking anymore,
                 // which means the Panel is at least partially expanded, then it means
@@ -1346,7 +1362,7 @@
             String selection, boolean selectionValid, float x, float y) {
         if (mIsAccessibilityModeEnabled) return;
 
-        if (mSearchPanel.isShowing()) {
+        if (mSearchPanel != null && mSearchPanel.isShowing()) {
             if (selectionValid) {
                 mSearchPanel.setSearchTerm(selection);
             } else {
@@ -1359,7 +1375,7 @@
     public void handleSelectionSuppression(BlacklistReason reason) {
         if (mIsAccessibilityModeEnabled) return;
 
-        mSearchPanel.getPanelMetrics().setBlacklistReason(reason);
+        if (mSearchPanel != null) mSearchPanel.getPanelMetrics().setBlacklistReason(reason);
     }
 
     // ============================================================================================
@@ -1431,11 +1447,9 @@
     private native long nativeInit();
     private native void nativeDestroy(long nativeContextualSearchManager);
     private native void nativeStartSearchTermResolutionRequest(long nativeContextualSearchManager,
-            String selection, String homeCountry, WebContents baseWebContents,
-            boolean maySendBasePageUrl);
+            ContextualSearchContext contextualSearchContext, WebContents baseWebContents);
     protected native void nativeGatherSurroundingText(long nativeContextualSearchManager,
-            String selection, String homeCountry, WebContents baseWebContents,
-            boolean maySendBasePageUrl);
+            ContextualSearchContext contextualSearchContext, WebContents baseWebContents);
     private native void nativeEnableContextualSearchJsApiForOverlay(
             long nativeContextualSearchManager, WebContents overlayWebContents);
     // Don't call these directly, instead call the private methods that cache the results.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index 161f317..86b1108 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content_public.browser.GestureStateListener;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.touch_selection.SelectionEventType;
 
 import java.util.regex.Matcher;
@@ -355,10 +356,11 @@
 
     /**
      * Handles an unhandled tap gesture.
+     * @param x The x coordinate in px.
+     * @param y The y coordinate in px.
      */
     void handleShowUnhandledTapUIIfNeeded(int x, int y) {
         mWasTapGestureDetected = false;
-        // TODO(donnd): shouldn't we check == TAP here instead of LONG_PRESS?
         // TODO(donnd): refactor to avoid needing a new handler API method as suggested by Pedro.
         if (mSelectionType != SelectionType.LONG_PRESS) {
             mWasTapGestureDetected = true;
@@ -403,14 +405,25 @@
     }
 
     /**
+     * Gets the base page ContentViewCore.
+     * Deprecated, use getBaseWebContents instead.
      * @return The Base Page's {@link ContentViewCore}, or {@code null} if there is no current tab.
      */
+    @Deprecated
     ContentViewCore getBaseContentView() {
         Tab currentTab = mActivity.getActivityTab();
         return currentTab != null ? currentTab.getContentViewCore() : null;
     }
 
     /**
+     * @return The Base Page's {@link WebContents}, or {@code null} if there is no current tab.
+     */
+    WebContents getBaseWebContents() {
+        Tab currentTab = mActivity.getActivityTab();
+        return currentTab != null ? currentTab.getContentViewCore().getWebContents() : null;
+    }
+
+    /**
      * Expands the current selection by the specified amounts.
      * @param selectionStartAdjust The start offset adjustment of the selection to use to highlight
      *                             the search term.
@@ -422,10 +435,10 @@
         // crbug.com/508354
 
         if (selectionStartAdjust == 0 && selectionEndAdjust == 0) return;
-        ContentViewCore basePageContentView = getBaseContentView();
-        if (basePageContentView != null && basePageContentView.getWebContents() != null) {
+        WebContents basePageWebContents = getBaseWebContents();
+        if (basePageWebContents != null) {
             mDidExpandSelection = true;
-            basePageContentView.getWebContents().adjustSelectionByCharacterOffset(
+            basePageWebContents.adjustSelectionByCharacterOffset(
                     selectionStartAdjust, selectionEndAdjust);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
index 98e963a..477326f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -164,10 +164,6 @@
                 removeSection(mSections.get(category));
                 return;
 
-            case CategoryStatus.SIGNED_OUT:
-                resetSection(category, status, /* alwaysAllowEmptySections = */ false);
-                return;
-
             default:
                 mSections.get(category).setStatus(status);
                 return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
index 68f0ca8..767af465 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -42,7 +42,6 @@
             case CategoryStatus.INITIALIZING:
             case CategoryStatus.AVAILABLE:
             case CategoryStatus.AVAILABLE_LOADING:
-            case CategoryStatus.SIGNED_OUT:
                 return true;
         }
         return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
index a212b08..c23bdaa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
@@ -33,6 +33,9 @@
     private final VoiceSuggestionProvider mVoiceSuggestionProvider = new VoiceSuggestionProvider();
 
 
+    private boolean mUseCachedZeroSuggestResults;
+    private boolean mWaitingForSuggestionsToCache;
+
     /**
      * Listener for receiving OmniboxSuggestions.
      */
@@ -73,6 +76,17 @@
     }
 
     /**
+     * Use cached zero suggest results if there are any available and start caching them
+     * for all zero suggest updates.
+     */
+    public void startCachedZeroSuggest() {
+        mUseCachedZeroSuggestResults = true;
+        List<OmniboxSuggestion> suggestions =
+                OmniboxSuggestion.getCachedOmniboxSuggestionsForZeroSuggest();
+        if (suggestions != null) mListener.onSuggestionsReceived(suggestions, "");
+    }
+
+    /**
      * Starts querying for omnibox suggestions for a given text.
      *
      * @param profile The profile to use for starting the AutocompleteController
@@ -103,6 +117,7 @@
         if (mNativeAutocompleteControllerAndroid != 0) {
             nativeStart(mNativeAutocompleteControllerAndroid, text, cursorPosition, null, url,
                     preventInlineAutocomplete, false, false, true);
+            mWaitingForSuggestionsToCache = false;
         }
     }
 
@@ -140,6 +155,7 @@
         if (profile == null || TextUtils.isEmpty(url)) return;
         mNativeAutocompleteControllerAndroid = nativeInit(profile);
         if (mNativeAutocompleteControllerAndroid != 0) {
+            if (mUseCachedZeroSuggestResults) mWaitingForSuggestionsToCache = true;
             nativeOnOmniboxFocused(mNativeAutocompleteControllerAndroid, omniboxText, url,
                     focusedFromFakebox);
         }
@@ -159,6 +175,7 @@
     public void stop(boolean clear) {
         if (clear) mVoiceSuggestionProvider.clearVoiceSearchResults();
         mCurrentNativeAutocompleteResult = 0;
+        mWaitingForSuggestionsToCache = false;
         if (mNativeAutocompleteControllerAndroid != 0) {
             nativeStop(mNativeAutocompleteControllerAndroid, clear);
         }
@@ -210,6 +227,9 @@
 
         // Notify callbacks of suggestions.
         mListener.onSuggestionsReceived(suggestions, inlineAutocompleteText);
+        if (mWaitingForSuggestionsToCache) {
+            OmniboxSuggestion.cacheOmniboxSuggestionListForZeroSuggest(suggestions);
+        }
     }
 
     @CalledByNative
@@ -234,6 +254,7 @@
     public void onSuggestionSelected(int selectedIndex, int type,
             String currentPageUrl, boolean focusedFromFakebox, long elapsedTimeSinceModified,
             int completedLength, WebContents webContents) {
+        assert mNativeAutocompleteControllerAndroid != 0;
         // Don't natively log voice suggestion results as we add them in Java.
         if (type == OmniboxSuggestionType.VOICE_SUGGEST) return;
         nativeOnSuggestionSelected(mNativeAutocompleteControllerAndroid, selectedIndex,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 26dcfc4..0ff7e03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -226,6 +226,36 @@
 
     private Runnable mShowSuggestions;
 
+    private boolean mShowCachedZeroSuggestResults;
+
+    private DeferredOnSelectionRunnable mDeferredOnSelection;
+
+    private abstract class DeferredOnSelectionRunnable implements Runnable {
+        protected final OmniboxSuggestion mSuggestion;
+        protected final int mPosition;
+        protected boolean mShouldLog;
+
+        public DeferredOnSelectionRunnable(OmniboxSuggestion suggestion, int position) {
+            this.mSuggestion = suggestion;
+            this.mPosition = position;
+        }
+
+        /**
+         * Set whether the selection matches with native results for logging to make sense.
+         * @param log Whether the selection should be logged in native code.
+         */
+        public void setShouldLog(boolean log) {
+            mShouldLog = log;
+        }
+
+        /**
+         * @return Whether the selection should be logged in native code.
+         */
+        public boolean shouldLog() {
+            return mShouldLog;
+        }
+    }
+
     /**
      * Listener for receiving the messages related with interacting with the omnibox during startup.
      */
@@ -761,6 +791,29 @@
         // If the user focused the omnibox prior to the native libraries being initialized,
         // autocomplete will not always be enabled, so we force it enabled in that case.
         mUrlBar.setIgnoreTextChangesForAutocomplete(false);
+        mAutocomplete = new AutocompleteController(this);
+    }
+
+    /**
+     * Sets to show cached zero suggest results. This will start both caching zero suggest results
+     * in shared preferences and also attempt to show them when appropriate without needing native
+     * initialization. See {@link LocationBarLayout#showCachedZeroSuggestResultsIfAvailable()} for
+     * showing the loaded results before native initialization.
+     * @param showCachedZeroSuggestResults Whether cached zero suggest should be shown.
+     */
+    public void setShowCachedZeroSuggestResults(boolean showCachedZeroSuggestResults) {
+        mShowCachedZeroSuggestResults = showCachedZeroSuggestResults;
+        if (mShowCachedZeroSuggestResults) mAutocomplete.startCachedZeroSuggest();
+    }
+
+    /**
+     * Signals the omnibox to shows the cached zero suggest results if they have been loaded from
+     * cache successfully.
+     */
+    public void showCachedZeroSuggestResultsIfAvailable() {
+        if (!mShowCachedZeroSuggestResults || mSuggestionList == null) return;
+        setSuggestionsListVisibility(true);
+        mSuggestionList.updateLayoutParams();
     }
 
     /**
@@ -794,8 +847,6 @@
         mDeleteButton.setOnClickListener(this);
         mMicButton.setOnClickListener(this);
 
-        mAutocomplete = new AutocompleteController(this);
-
         mOmniboxPrerender = new OmniboxPrerender();
 
         for (Runnable deferredRunnable : mDeferredNativeRunnables) {
@@ -1548,6 +1599,15 @@
             @Override
             public void onSelection(OmniboxSuggestion suggestion, int position) {
                 mSuggestionSelectionInProgress = true;
+                if (mShowCachedZeroSuggestResults && !mNativeInitialized) {
+                    mDeferredOnSelection = new DeferredOnSelectionRunnable(suggestion, position) {
+                        @Override
+                        public void run() {
+                            onSelection(this.mSuggestion, this.mPosition);
+                        }
+                    };
+                    return;
+                }
                 String suggestionMatchUrl = updateSuggestionUrlIfNeeded(
                         suggestion, position, false);
                 loadUrlFromOmniboxMatch(suggestionMatchUrl, suggestion.getTransition(), position,
@@ -1720,7 +1780,7 @@
      */
     @Override
     public void hideSuggestions() {
-        if (mAutocomplete == null) return;
+        if (mAutocomplete == null || !mNativeInitialized) return;
 
         if (mShowSuggestions != null) removeCallbacks(mShowSuggestions);
 
@@ -1861,12 +1921,13 @@
         // so can only be called once the native side is set up.
         assert mNativeInitialized : "Suggestions received before native side intialialized";
 
-        if (getCurrentTab() == null) {
-            // If the current tab is not available, drop the suggestions and hide the autocomplete.
-            hideSuggestions();
-            return;
+        if (mDeferredOnSelection != null) {
+            mDeferredOnSelection.setShouldLog(newSuggestions.size() > mDeferredOnSelection.mPosition
+                    && mDeferredOnSelection.mSuggestion.equals(
+                            newSuggestions.get(mDeferredOnSelection.mPosition)));
+            mDeferredOnSelection.run();
+            mDeferredOnSelection = null;
         }
-
         String userText = mUrlBar.getTextWithoutAutocomplete();
         mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText;
 
@@ -1945,7 +2006,8 @@
         // Update the navigation button to show the default suggestion's icon.
         updateNavigationButton();
 
-        if (!CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_INSTANT)
+        if (mNativeInitialized
+                && !CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_INSTANT)
                 && PrivacyPreferencesManager.getInstance().shouldPrerender()) {
             mOmniboxPrerender.prerenderMaybe(
                     userText,
@@ -2092,9 +2154,15 @@
         WebContents webContents = currentTab != null ? currentTab.getWebContents() : null;
         long elapsedTimeSinceModified = mNewOmniboxEditSessionTimestamp > 0
                 ? (SystemClock.elapsedRealtime() - mNewOmniboxEditSessionTimestamp) : -1;
-        mAutocomplete.onSuggestionSelected(matchPosition, type, currentPageUrl,
-                mUrlFocusedFromFakebox, elapsedTimeSinceModified, mUrlBar.getAutocompleteLength(),
-                webContents);
+        boolean shouldSkipNativeLog = mShowCachedZeroSuggestResults
+                && (mDeferredOnSelection != null)
+                && !mDeferredOnSelection.shouldLog();
+        if (!shouldSkipNativeLog) {
+            mAutocomplete.onSuggestionSelected(matchPosition, type, currentPageUrl,
+                    mUrlFocusedFromFakebox, elapsedTimeSinceModified,
+                    mUrlBar.getAutocompleteLength(),
+                    webContents);
+        }
         loadUrl(url, transition);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestion.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestion.java
index f8c81b2..06a942f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestion.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestion.java
@@ -4,10 +4,14 @@
 
 package org.chromium.chrome.browser.omnibox;
 
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 import android.text.TextUtils;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -15,6 +19,16 @@
  */
 @VisibleForTesting
 public class OmniboxSuggestion {
+    private static final String KEY_ZERO_SUGGEST_LIST_SIZE = "zero_suggest_list_size";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_URL = "zero_suggest_url";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_DISPLAY_TEST = "zero_suggest_display_text";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_DESCRIPTION = "zero_suggest_description";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_NATIVE_TYPE = "zero_suggest_native_type";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_IS_SEARCH_TYPE = "zero_suggest_is_search";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_ANSWER_TEXT = "zero_suggest_answer_text";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_ANSWER_TYPE = "zero_suggest_answer_type";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_IS_DELETABLE = "zero_suggest_is_deletable";
+    private static final String KEY_PREFIX_ZERO_SUGGEST_IS_STARRED = "zero_suggest_is_starred";
 
     /**
      * Specifies the style of portions of the suggestion text.
@@ -192,4 +206,65 @@
                 && mIsStarred == suggestion.mIsStarred
                 && mIsDeletable == suggestion.mIsDeletable;
     }
+
+    /**
+     * Cache the given suggestion list in shared preferences.
+     * @param suggestions Suggestions to be cached.
+     */
+    public static void cacheOmniboxSuggestionListForZeroSuggest(
+            List<OmniboxSuggestion> suggestions) {
+        Editor editor = ContextUtils.getAppSharedPreferences().edit();
+        editor.putInt(KEY_ZERO_SUGGEST_LIST_SIZE, suggestions.size()).apply();
+        for (int i = 0; i < suggestions.size(); i++) {
+            OmniboxSuggestion suggestion = suggestions.get(i);
+            editor.putString(KEY_PREFIX_ZERO_SUGGEST_URL + i, suggestion.getUrl())
+                    .putString(KEY_PREFIX_ZERO_SUGGEST_DISPLAY_TEST + i,
+                            suggestion.getDisplayText())
+                    .putString(KEY_PREFIX_ZERO_SUGGEST_DESCRIPTION + i,
+                            suggestion.getDescription())
+                    .putString(KEY_PREFIX_ZERO_SUGGEST_ANSWER_TEXT + i,
+                            suggestion.getAnswerContents())
+                    .putString(KEY_PREFIX_ZERO_SUGGEST_ANSWER_TYPE + i,
+                            suggestion.getAnswerType())
+                    .putInt(KEY_PREFIX_ZERO_SUGGEST_NATIVE_TYPE + i, suggestion.getType())
+                    .putBoolean(KEY_PREFIX_ZERO_SUGGEST_IS_SEARCH_TYPE + i,
+                            !suggestion.isUrlSuggestion())
+                    .putBoolean(KEY_PREFIX_ZERO_SUGGEST_IS_DELETABLE + i, suggestion.mIsDeletable)
+                    .putBoolean(KEY_PREFIX_ZERO_SUGGEST_IS_STARRED + i, suggestion.mIsStarred)
+                    .apply();
+        }
+    }
+
+    /**
+     * @return The zero suggest result if they have been cached before.
+     */
+    public static List<OmniboxSuggestion> getCachedOmniboxSuggestionsForZeroSuggest() {
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        int size = prefs.getInt(KEY_ZERO_SUGGEST_LIST_SIZE, -1);
+        List<OmniboxSuggestion> suggestions = null;
+        if (size > 1) {
+            suggestions = new ArrayList<>(size);
+            List<MatchClassification> classifications = new ArrayList<>();
+            classifications.add(new MatchClassification(0, MatchClassificationStyle.NONE));
+            for (int i = 0; i < size; i++) {
+                String url = prefs.getString(KEY_PREFIX_ZERO_SUGGEST_URL + i, "");
+                String displayText = prefs.getString(KEY_PREFIX_ZERO_SUGGEST_DISPLAY_TEST + i, "");
+                String description = prefs.getString(KEY_PREFIX_ZERO_SUGGEST_DESCRIPTION + i, "");
+                String answerText = prefs.getString(KEY_PREFIX_ZERO_SUGGEST_ANSWER_TEXT + i, "");
+                String answerType = prefs.getString(KEY_PREFIX_ZERO_SUGGEST_ANSWER_TYPE + i, "");
+                int nativeType = prefs.getInt(KEY_PREFIX_ZERO_SUGGEST_NATIVE_TYPE + i, -1);
+                boolean isSearchType =
+                        prefs.getBoolean(KEY_PREFIX_ZERO_SUGGEST_IS_SEARCH_TYPE, true);
+                boolean isStarred = prefs.getBoolean(KEY_PREFIX_ZERO_SUGGEST_IS_STARRED + i, false);
+                boolean isDeletable = prefs.getBoolean(
+                        KEY_PREFIX_ZERO_SUGGEST_IS_DELETABLE + i, false);
+                OmniboxSuggestion suggestion = new OmniboxSuggestion(
+                        nativeType, !isSearchType, 0, 0, displayText,
+                        classifications, description, classifications, answerText,
+                        answerType, "", url, isStarred, isDeletable);
+                suggestions.add(suggestion);
+            }
+        }
+        return suggestions;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
index 70e06f5..7373c972 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
@@ -31,7 +31,6 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.BuildInfo;
-import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
@@ -712,10 +711,7 @@
         if (mCurrentSyncError == SYNC_AUTH_ERROR) {
             AccountManagerHelper.get(getActivity())
                     .updateCredentials(ChromeSigninController.get(getActivity()).getSignedInUser(),
-                            getActivity(), new Callback<Boolean>() {
-                                @Override
-                                public void onResult(Boolean result) {}
-                            });
+                            getActivity(), null);
             return;
         }
 
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index fd90721d..d3fae37 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -225,6 +225,7 @@
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuTitleView.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchBlacklist.java",
+  "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristic.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
index 13a271f..8978f83d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
@@ -114,8 +114,7 @@
 
         @Override
         protected void nativeGatherSurroundingText(long nativeContextualSearchManager,
-                String selection, String homeCountry, WebContents webContents,
-                boolean maySendBasePageUrl) {}
+                ContextualSearchContext contextualSearchContext, WebContents baseWebContents) {}
 
         /**
          * @return A stubbed ContentViewCore for mocking text selection.
@@ -140,7 +139,7 @@
         }
 
         @Override
-        public StubbedContentViewCore getBaseContentView() {
+        StubbedContentViewCore getBaseContentView() {
             return mContentViewCore;
         }
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index a3f3674..39397ca 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -277,15 +277,18 @@
         mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE);
         assertItemsFor(section(4));
 
-        // When the category is disabled, the suggestions are cleared and we should go back to
-        // the situation with the status card.
-        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.SIGNED_OUT);
-        assertItemsFor(sectionWithStatusCard());
+        // When the category is disabled, the section should go away completely.
+        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
+        assertItemsFor();
 
-        // The adapter should now be waiting for new suggestions.
+        // Now we're in the "all dismissed" state. No suggestions should be accepted.
         suggestions = createDummySuggestions(6, TEST_CATEGORY);
         mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions);
+        assertItemsFor();
+
+        // After a full refresh, the adapter should accept suggestions again.
+        mSource.fireFullRefreshRequired();
         assertItemsFor(section(6));
     }
 
@@ -306,20 +309,26 @@
         suggestions.add(new SnippetArticle(TEST_CATEGORY, "https://site.com/url1", "title1", "pub1",
                 "txt1", "https://site.com/url1", 0, 0, 0));
 
-        // When suggestion are disabled, we should not be able to load them.
-        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.SIGNED_OUT);
+        // When the provider is removed, we should not be able to load suggestions. The UI should
+        // stay the same though.
+        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED);
         mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions);
-        assertItemsFor(sectionWithStatusCard());
+        assertItemsFor(section(3));
 
-        // INITIALIZING lets us load suggestion still.
+        // INITIALIZING lets us load suggestions still.
         mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INITIALIZING);
         mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions);
         assertItemsFor(sectionWithStatusCard().withProgress());
 
-        // The adapter should now be waiting for new suggestion and the fourth one should appear.
+        // The adapter should now be waiting for new suggestions and the fourth one should appear.
         mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions);
         assertItemsFor(section(4));
+
+        // When the category gets disabled, the section should go away and not load any suggestions.
+        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
+        mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions);
+        assertItemsFor();
     }
 
     /**
@@ -341,8 +350,10 @@
         mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE_LOADING);
         assertTrue(progress.isVisible());
 
-        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.SIGNED_OUT);
-        assertFalse(progress.isVisible());
+        // After the section gets disabled, it should gone completely, so checking the progress
+        // indicator doesn't make sense anymore.
+        mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
+        assertEquals(mAdapter.getSectionListForTesting().getSectionForTesting(TEST_CATEGORY), null);
     }
 
     /**
@@ -733,11 +744,12 @@
         suggestionsSource.setSuggestionsForCategory(
                 TEST_CATEGORY, createDummySuggestions(0, TEST_CATEGORY));
         mAdapter.getSectionListForTesting().onCategoryStatusChanged(
-                TEST_CATEGORY, CategoryStatus.SIGNED_OUT);
-        verify(dataObserver).onItemRangeRemoved(2, newSuggestionCount);
-        verify(dataObserver).onItemRangeChanged(4, 1, null); // Spacer refresh
-        verify(dataObserver).onItemRangeInserted(2, 1); // Status card added
-        verify(dataObserver).onItemRangeChanged(5, 1, null); // Spacer refresh
+                TEST_CATEGORY, CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
+        // All suggestions as well as the header and the action should be gone.
+        verify(dataObserver).onItemRangeRemoved(1, newSuggestionCount + 2);
+        // The spacer gets refreshed twice: Once when the section is removed, and then again when
+        // the "all dismissed" item gets added.
+        verify(dataObserver, times(2)).onItemRangeChanged(2, 1, null);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
index ff940e63..044021ff 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -183,7 +183,7 @@
         verifyNoMoreInteractions(mParent);
 
         // We clear existing suggestions when the status is not AVAILABLE, and show the status card.
-        section.setStatus(CategoryStatus.SIGNED_OUT);
+        section.setStatus(CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
         verify(mParent).onItemRangeRemoved(section, 1, suggestionCount);
         verify(mParent).onItemRangeInserted(section, 1, 1);
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a24533f..9cdec6b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3985,6 +3985,7 @@
       "../android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java",
       "../android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java",
       "../android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java",
+      "../android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java",
       "../android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java",
       "../android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java",
       "../android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java",
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index d256c67a..bc699aab 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h"
 #include "chrome/browser/android/compositor/scene_layer/toolbar_scene_layer.h"
 #include "chrome/browser/android/compositor/tab_content_manager.h"
+#include "chrome/browser/android/contextualsearch/contextual_search_context.h"
 #include "chrome/browser/android/contextualsearch/contextual_search_manager.h"
 #include "chrome/browser/android/contextualsearch/contextual_search_tab_helper.h"
 #include "chrome/browser/android/contextualsearch/ctr_suppression.h"
@@ -267,6 +268,7 @@
     {"ContentSuggestionsNotificationHelper",
      ntp_snippets::ContentSuggestionsNotificationHelper::Register},
     {"ContextMenuHelper", RegisterContextMenuHelper},
+    {"ContextualSearchContext", RegisterContextualSearchContext},
     {"ContextualSearchManager", RegisterContextualSearchManager},
     {"ContextualSearchSceneLayer", RegisterContextualSearchSceneLayer},
     {"ContextualSearchTabHelper", RegisterContextualSearchTabHelper},
diff --git a/chrome/browser/android/contextualsearch/contextual_search_context.cc b/chrome/browser/android/contextualsearch/contextual_search_context.cc
index e0db7cc..82900a6 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_context.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_context.cc
@@ -2,7 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/contextualsearch/contextual_search_context.h"
+#include <chrome/browser/android/contextualsearch/contextual_search_context.h>
+
+#include "base/android/jni_string.h"
+#include "content/public/browser/browser_thread.h"
+
+#include "jni/ContextualSearchContext_jni.h"
+
+ContextualSearchContext::ContextualSearchContext(JNIEnv* env, jobject obj)
+    : can_resolve(false),
+      can_send_base_page_url(false),
+      start_offset(0),
+      end_offset(0) {
+  java_object_.Reset(env, obj);
+}
 
 ContextualSearchContext::ContextualSearchContext(
     const std::string& selected_text,
@@ -11,8 +24,111 @@
     const std::string& encoding)
     : selected_text(selected_text),
       home_country(home_country),
-      page_url(page_url),
-      encoding(encoding) {}
+      base_page_url(page_url),
+      base_page_encoding(encoding) {
+  java_object_ = nullptr;
+}
 
 ContextualSearchContext::~ContextualSearchContext() {
 }
+
+// static
+ContextualSearchContext*
+ContextualSearchContext::FromJavaContextualSearchContext(
+    const base::android::JavaRef<jobject>& j_contextual_search_context) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (j_contextual_search_context.is_null())
+    return NULL;
+
+  ContextualSearchContext* contextual_search_context =
+      reinterpret_cast<ContextualSearchContext*>(
+          Java_ContextualSearchContext_getNativePointer(
+              base::android::AttachCurrentThread(),
+              j_contextual_search_context));
+  return contextual_search_context;
+}
+
+void ContextualSearchContext::SetResolveProperties(
+    JNIEnv* env,
+    jobject obj,
+    const base::android::JavaParamRef<jstring>& j_selection,
+    const base::android::JavaParamRef<jstring>& j_home_country,
+    jboolean j_may_send_base_page_url) {
+  can_resolve = true;
+  selected_text = base::android::ConvertJavaStringToUTF8(env, j_selection);
+  home_country = base::android::ConvertJavaStringToUTF8(env, j_home_country);
+  can_send_base_page_url = j_may_send_base_page_url;
+}
+
+// Accessors
+
+bool ContextualSearchContext::CanResolve() const {
+  return can_resolve;
+}
+
+bool ContextualSearchContext::CanSendBasePageUrl() const {
+  return can_send_base_page_url;
+}
+
+const GURL ContextualSearchContext::GetBasePageUrl() const {
+  return base_page_url;
+}
+
+void ContextualSearchContext::SetBasePageUrl(const GURL& base_page_url) {
+  this->base_page_url = base_page_url;
+}
+
+const std::string ContextualSearchContext::GetBasePageEncoding() const {
+  return base_page_encoding;
+}
+
+void ContextualSearchContext::SetBasePageEncoding(
+    const std::string& base_page_encoding) {
+  this->base_page_encoding = base_page_encoding;
+}
+
+const std::string ContextualSearchContext::GetHomeCountry() const {
+  return home_country;
+}
+
+void ContextualSearchContext::SetSelectionSurroundings(
+    int start_offset,
+    int end_offset,
+    const base::string16& surrounding_text) {
+  this->start_offset = start_offset;
+  this->end_offset = end_offset;
+  this->surrounding_text = surrounding_text;
+}
+
+const std::string ContextualSearchContext::GetOriginalSelectedText() const {
+  return selected_text;
+}
+
+const base::string16 ContextualSearchContext::GetSurroundingText() const {
+  return surrounding_text;
+}
+
+int ContextualSearchContext::GetStartOffset() const {
+  return start_offset;
+}
+
+int ContextualSearchContext::GetEndOffset() const {
+  return end_offset;
+}
+
+// Java wrapper boilerplate
+
+void ContextualSearchContext::Destroy(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  delete this;
+}
+
+bool RegisterContextualSearchContext(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+jlong Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
+  ContextualSearchContext* context = new ContextualSearchContext(env, obj);
+  return reinterpret_cast<intptr_t>(context);
+}
diff --git a/chrome/browser/android/contextualsearch/contextual_search_context.h b/chrome/browser/android/contextualsearch/contextual_search_context.h
index 436a114..350d962 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_context.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_context.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/android/jni_android.h"
 #include "base/macros.h"
 #include "url/gurl.h"
 
@@ -14,22 +15,88 @@
 // text.
 struct ContextualSearchContext {
  public:
+  ContextualSearchContext(JNIEnv* env, jobject obj);
+  // Constructor for tests.
   ContextualSearchContext(const std::string& selected_text,
                           const std::string& home_country,
                           const GURL& page_url,
                           const std::string& encoding);
   ~ContextualSearchContext();
 
-  const std::string selected_text;
-  const std::string home_country;
-  const GURL page_url;
-  const std::string encoding;
+  // Calls the destructor.  Should be called when this native object is no
+  // longer needed.
+  void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
 
+  // Returns the native |ContextualSearchContext| given the Java object.
+  static ContextualSearchContext* FromJavaContextualSearchContext(
+      const base::android::JavaRef<jobject>& j_contextual_search_context);
+
+  // Returns whether this context can be resolved.
+  // The context can be resolved only after calling SetResolveProperites.
+  bool CanResolve() const;
+
+  // Returns whether the base page URL may be sent (according to the Java
+  // policy).
+  bool CanSendBasePageUrl() const;
+
+  // Sets the properties needed to resolve a context.
+  void SetResolveProperties(
+      JNIEnv* env,
+      jobject obj,
+      const base::android::JavaParamRef<jstring>& j_selection,
+      const base::android::JavaParamRef<jstring>& j_home_country,
+      jboolean j_may_send_base_page_url);
+
+  // Gets the URL of the base page.
+  const GURL GetBasePageUrl() const;
+  // Sets the URL of the base page.
+  void SetBasePageUrl(const GURL& base_page_url);
+
+  // Gets the encoding of the base page.  This is not very important, since
+  // the surrounding text stored here in a base::string16 is implicitly encoded
+  // in UTF-16 (see http://www.chromium.org/developers/chromium-string-usage).
+  const std::string GetBasePageEncoding() const;
+  void SetBasePageEncoding(const std::string& base_page_encoding);
+
+  // Gets the country code of the home country of the user, or an empty string.
+  const std::string GetHomeCountry() const;
+
+  // Sets the selection and surroundings.
+  void SetSelectionSurroundings(int start_offset,
+                                int end_offset,
+                                const base::string16& surrounding_text);
+
+  // Gets the original selection.
+  const std::string GetOriginalSelectedText() const;
+
+  // Gets the text surrounding the selection (including the selection).
+  const base::string16 GetSurroundingText() const;
+
+  // Gets the start offset of the selection within the surrounding text (in
+  // characters).
+  int GetStartOffset() const;
+  // Gets the end offset of the selection within the surrounding text (in
+  // characters).
+  int GetEndOffset() const;
+
+ private:
+  bool can_resolve;
+  bool can_send_base_page_url;
+
+  std::string selected_text;
+  std::string home_country;
+  GURL base_page_url;
+  std::string base_page_encoding;
   base::string16 surrounding_text;
   int start_offset;
   int end_offset;
 
+  // The linked Java object.
+  base::android::ScopedJavaGlobalRef<jobject> java_object_;
+
   DISALLOW_COPY_AND_ASSIGN(ContextualSearchContext);
 };
 
+bool RegisterContextualSearchContext(JNIEnv* env);
+
 #endif  // CHROME_BROWSER_ANDROID_CONTEXTUALSEARCH_CONTEXTUAL_SEARCH_CONTEXT_H_
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index bde0a2f5..6ed1466 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -98,33 +98,53 @@
 ContextualSearchDelegate::~ContextualSearchDelegate() {
 }
 
-void ContextualSearchDelegate::StartSearchTermResolutionRequest(
-    const std::string& selection,
-    const std::string& home_country,
-    content::WebContents* web_contents,
-    bool may_send_base_page_url) {
-  GatherSurroundingTextWithCallback(
-      selection, home_country, web_contents, may_send_base_page_url,
-      base::Bind(&ContextualSearchDelegate::StartSearchTermRequestFromSelection,
-                 AsWeakPtr()));
-}
-
 void ContextualSearchDelegate::GatherAndSaveSurroundingText(
-    const std::string& selection,
-    const std::string& home_country,
-    content::WebContents* web_contents,
-    bool may_send_base_page_url) {
-  GatherSurroundingTextWithCallback(
-      selection, home_country, web_contents, may_send_base_page_url,
-      base::Bind(&ContextualSearchDelegate::SaveSurroundingText, AsWeakPtr()));
-  // TODO(donnd): clear the context here, since we're done with it (but risky).
+    ContextualSearchContext* contextual_search_context,
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  RenderFrameHost::TextSurroundingSelectionCallback callback =
+      base::Bind(&ContextualSearchDelegate::OnTextSurroundingSelectionAvailable,
+                 AsWeakPtr());
+  context_ = contextual_search_context;
+  context_->SetBasePageEncoding(web_contents->GetEncoding());
+  int surroundingTextSize = context_->CanResolve()
+                                ? field_trial_->GetSurroundingSize()
+                                : field_trial_->GetIcingSurroundingSize();
+  RenderFrameHost* focused_frame = web_contents->GetFocusedFrame();
+  if (focused_frame) {
+    focused_frame->RequestTextSurroundingSelection(callback,
+                                                   surroundingTextSize);
+  } else {
+    callback.Run(base::string16(), 0, 0);
+  }
 }
 
-void ContextualSearchDelegate::ContinueSearchTermResolutionRequest() {
-  DCHECK(context_.get());
-  if (!context_.get())
+void ContextualSearchDelegate::StartSearchTermResolutionRequest(
+    ContextualSearchContext* contextual_search_context,
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  DCHECK(context_ == contextual_search_context);
+  DCHECK(context_->CanResolve());
+
+  // Immediately cancel any request that's in flight, since we're building a new
+  // context (and the response disposes of any existing context).
+  search_term_fetcher_.reset();
+
+  // Decide if the URL should be sent with the context.
+  GURL page_url(web_contents->GetURL());
+  if (context_->CanSendBasePageUrl() &&
+      CanSendPageURL(page_url, ProfileManager::GetActiveUserProfile(),
+                     template_url_service_)) {
+    context_->SetBasePageUrl(page_url);
+  }
+  ResolveSearchTermFromContext();
+}
+
+void ContextualSearchDelegate::ResolveSearchTermFromContext() {
+  DCHECK(context_);
+  if (!context_)
     return;
-  GURL request_url(BuildRequestUrl(context_->home_country));
+  GURL request_url(BuildRequestUrl(context_->GetHomeCountry()));
   DCHECK(request_url.is_valid());
 
   // Reset will delete any previous fetcher, and we won't get any callback.
@@ -166,9 +186,6 @@
     }
   }
   search_term_callback_.Run(*resolved_search_term);
-
-  // The ContextualSearchContext is consumed once the request has completed.
-  context_.reset();
 }
 
 std::unique_ptr<ResolvedSearchTerm>
@@ -201,13 +218,13 @@
     // the new and old selection.
     if (mention_start >= mention_end ||
         (mention_end - mention_start) > kContextualSearchMaxSelection ||
-        mention_end <= context_->start_offset ||
-        mention_start >= context_->end_offset) {
+        mention_end <= context_->GetStartOffset() ||
+        mention_start >= context_->GetEndOffset()) {
       start_adjust = 0;
       end_adjust = 0;
     } else {
-      start_adjust = mention_start - context_->start_offset;
-      end_adjust = mention_end - context_->end_offset;
+      start_adjust = mention_start - context_->GetStartOffset();
+      end_adjust = mention_end - context_->GetEndOffset();
     }
   }
   bool is_invalid = response_code == net::URLFetcher::RESPONSE_CODE_INVALID;
@@ -270,107 +287,64 @@
   return request;
 }
 
-void ContextualSearchDelegate::GatherSurroundingTextWithCallback(
-    const std::string& selection,
-    const std::string& home_country,
-    content::WebContents* web_contents,
-    bool may_send_base_page_url,
-    HandleSurroundingsCallback callback) {
-  // Immediately cancel any request that's in flight, since we're building a new
-  // context (and the response disposes of any existing context).
-  search_term_fetcher_.reset();
-  BuildContext(selection, home_country, web_contents, may_send_base_page_url);
-  DCHECK(web_contents);
-  RenderFrameHost* focused_frame = web_contents->GetFocusedFrame();
-  if (focused_frame) {
-    focused_frame->RequestTextSurroundingSelection(
-        callback, field_trial_->GetSurroundingSize());
-  } else {
-    callback.Run(base::string16(), 0, 0);
-  }
-}
-
-void ContextualSearchDelegate::BuildContext(const std::string& selection,
-                                            const std::string& home_country,
-                                            content::WebContents* web_contents,
-                                            bool may_send_base_page_url) {
-  // Decide if the URL should be sent with the context.
-  GURL page_url(web_contents->GetURL());
-  GURL url_to_send;
-  if (may_send_base_page_url &&
-      CanSendPageURL(page_url, ProfileManager::GetActiveUserProfile(),
-                     template_url_service_)) {
-    url_to_send = page_url;
-  }
-  std::string encoding(web_contents->GetEncoding());
-  context_.reset(new ContextualSearchContext(selection, home_country,
-                                             url_to_send, encoding));
-}
-
-void ContextualSearchDelegate::StartSearchTermRequestFromSelection(
+void ContextualSearchDelegate::OnTextSurroundingSelectionAvailable(
     const base::string16& surrounding_text,
     int start_offset,
     int end_offset) {
-  // TODO(donnd): figure out how to gather text surrounding the selection
-  // for other purposes too: e.g. to determine if we should select the
-  // word where the user tapped.
-  if (context_.get()) {
-    SaveSurroundingText(surrounding_text, start_offset, end_offset);
+  SaveSurroundingText(surrounding_text, start_offset, end_offset);
+  if (context_->CanResolve())
     SendSurroundingText(kSurroundingSizeForUI);
-    ContinueSearchTermResolutionRequest();
-  } else {
-    DVLOG(1) << "ctxs: Null context, ignored!";
-  }
 }
 
 void ContextualSearchDelegate::SaveSurroundingText(
     const base::string16& surrounding_text,
     int start_offset,
     int end_offset) {
-  DCHECK(context_.get());
+  DCHECK(context_);
   // Sometimes the surroundings are 0, 0, '', so fall back on the selection.
   // See crbug.com/393100.
+  bool use_selection = false;
   if (start_offset == 0 && end_offset == 0 && surrounding_text.length() == 0) {
-    context_->surrounding_text = base::UTF8ToUTF16(context_->selected_text);
-    context_->start_offset = 0;
-    context_->end_offset = context_->selected_text.length();
-  } else {
-    context_->surrounding_text = surrounding_text;
-    context_->start_offset = start_offset;
-    context_->end_offset = end_offset;
+    use_selection = true;
+    end_offset = context_->GetOriginalSelectedText().length();
   }
+  const base::string16& surrounding_text_or_selection(
+      use_selection ? base::UTF8ToUTF16(context_->GetOriginalSelectedText())
+                    : surrounding_text);
 
   // Pin the start and end offsets to ensure they point within the string.
-  int surrounding_length = context_->surrounding_text.length();
-  context_->start_offset =
-      std::min(surrounding_length, std::max(0, context_->start_offset));
-  context_->end_offset =
-      std::min(surrounding_length, std::max(0, context_->end_offset));
+  int surrounding_length = surrounding_text_or_selection.length();
+  start_offset = std::min(surrounding_length, std::max(0, start_offset));
+  end_offset = std::min(surrounding_length, std::max(0, end_offset));
+
+  context_->SetSelectionSurroundings(start_offset, end_offset,
+                                     surrounding_text_or_selection);
 
   // Call the Icing callback with a shortened copy of the surroundings.
   int icing_surrounding_size = field_trial_->GetIcingSurroundingSize();
-  size_t selection_start = context_->start_offset;
-  size_t selection_end = context_->end_offset;
-  if (icing_surrounding_size >= 0 && selection_start < selection_end) {
+  size_t selection_start = start_offset;
+  size_t selection_end = end_offset;
+  if (icing_surrounding_size >= 0 && selection_start <= selection_end) {
     int icing_padding_each_side = icing_surrounding_size / 2;
     base::string16 icing_surrounding_text = SurroundingTextForIcing(
-        context_->surrounding_text, icing_padding_each_side, &selection_start,
-        &selection_end);
-    if (selection_start < selection_end)
-      icing_callback_.Run(context_->encoding, icing_surrounding_text,
-                          selection_start, selection_end);
+        surrounding_text_or_selection, icing_padding_each_side,
+        &selection_start, &selection_end);
+    if (selection_start <= selection_end)
+      icing_callback_.Run(context_->GetBasePageEncoding(),
+                          icing_surrounding_text, selection_start,
+                          selection_end);
   }
 }
 
 void ContextualSearchDelegate::SendSurroundingText(int max_surrounding_chars) {
-  const base::string16& surrounding = context_->surrounding_text;
+  const base::string16& surrounding = context_->GetSurroundingText();
 
   // Determine the text after the selection.
   int surrounding_length = surrounding.length();  // Cast to int.
   int num_after_characters = std::min(
-      surrounding_length - context_->end_offset, max_surrounding_chars);
-  base::string16 after_text = surrounding.substr(
-      context_->end_offset, num_after_characters);
+      surrounding_length - context_->GetEndOffset(), max_surrounding_chars);
+  base::string16 after_text =
+      surrounding.substr(context_->GetEndOffset(), num_after_characters);
 
   base::TrimWhitespace(after_text, base::TRIM_ALL, &after_text);
   surrounding_callback_.Run(UTF16ToUTF8(after_text));
@@ -385,15 +359,15 @@
     const ContextualSearchContext& context) {
   discourse_context::ClientDiscourseContext proto;
   discourse_context::Display* display = proto.add_display();
-  display->set_uri(context.page_url.spec());
+  display->set_uri(context.GetBasePageUrl().spec());
 
   discourse_context::Media* media = display->mutable_media();
-  media->set_mime_type(context.encoding);
+  media->set_mime_type(context.GetBasePageEncoding());
 
   discourse_context::Selection* selection = display->mutable_selection();
-  selection->set_content(UTF16ToUTF8(context.surrounding_text));
-  selection->set_start(context.start_offset);
-  selection->set_end(context.end_offset);
+  selection->set_content(UTF16ToUTF8(context.GetSurroundingText()));
+  selection->set_start(context.GetStartOffset());
+  selection->set_end(context.GetEndOffset());
   selection->set_is_uri_encoded(false);
 
   std::string serialized;
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.h b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
index dde551b3..bdec64a 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
@@ -43,11 +43,6 @@
   // Provides the Resolved Search Term, called when the Resolve Request returns.
   typedef base::Callback<void(const ResolvedSearchTerm&)>
       SearchTermResolutionCallback;
-  // Provides the surrounding text and selection start/end when Blink gathers
-  // surrounding text for the Context.
-  typedef base::Callback<
-      void(const base::string16&, int, int)>
-      HandleSurroundingsCallback;
   // Provides limited surrounding text for icing.
   typedef base::Callback<
       void(const std::string&, const base::string16&, size_t, size_t)>
@@ -66,24 +61,19 @@
       const IcingCallback& icing_callback);
   ~ContextualSearchDelegate() override;
 
-  // Gathers surrounding text and starts an asynchronous search term resolution
-  // request. The "search term" is the best query to issue for a section of text
-  // in the context of a web page. When the response is available the callback
-  // specified in the constructor is run.
-  void StartSearchTermResolutionRequest(const std::string& selection,
-                                        const std::string& home_country,
-                                        content::WebContents* web_contents,
-                                        bool may_send_base_page_url);
+  // Gathers surrounding text and saves it locally in the given context.
+  void GatherAndSaveSurroundingText(
+      ContextualSearchContext* contextual_search_context,
+      content::WebContents* web_contents);
 
-  // Gathers surrounding text and saves it locally for a future query.
-  void GatherAndSaveSurroundingText(const std::string& selection,
-                                    const std::string& home_country,
-                                    content::WebContents* web_contents,
-                                    bool may_send_base_page_url);
-
-  // Continues making a Search Term Resolution request, once the surrounding
-  // text has been gathered.
-  void ContinueSearchTermResolutionRequest();
+  // Starts an asynchronous search term resolution request.
+  // The given context includes some content from a web page and must be able
+  // to resolve.
+  // When the response is available the callback specified in the constructor
+  // is run.
+  void StartSearchTermResolutionRequest(
+      ContextualSearchContext* contextual_search_context,
+      content::WebContents* web_contents);
 
   // Gets the target language for translation purposes for this user.
   std::string GetTargetLanguage();
@@ -93,10 +83,15 @@
 
   // For testing.
   void set_context_for_testing(ContextualSearchContext* context) {
-    context_.reset(context);
+    context_ = context;
   }
 
  private:
+  // Friend our test which allows our private methods to be used in helper
+  // functions.  FRIEND_TEST_ALL_PREFIXES just friends individual prefixes.
+  // Needed for |ResolveSearchTermFromContext|.
+  friend class ContextualSearchDelegateTest;
+  // TODO(donnd): consider removing the following since the above covers this.
   FRIEND_TEST_ALL_PREFIXES(ContextualSearchDelegateTest,
                            SurroundingTextHighMaximum);
   FRIEND_TEST_ALL_PREFIXES(ContextualSearchDelegateTest,
@@ -118,12 +113,9 @@
   // net::URLFetcherDelegate:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
-  // Builds the ContextualSearchContext in the current context from
-  // the given parameters.
-  void BuildContext(const std::string& selection,
-                    const std::string& home_country,
-                    content::WebContents* web_contents,
-                    bool may_send_base_page_url);
+  // Resolves the search term specified by the current context.
+  // Only needed for tests.  TODO(donnd): make private and friend?
+  void ResolveSearchTermFromContext();
 
   // Builds and returns the search term resolution request URL.
   // |selection| is used as the default query.
@@ -136,17 +128,9 @@
       const std::string& base_page_url,
       const bool may_send_base_page_url);
 
-  // Will gather the surrounding text from the |content_view_core| and call the
-  // |callback|.
-  void GatherSurroundingTextWithCallback(const std::string& selection,
-                                         const std::string& home_country,
-                                         content::WebContents* web_contents,
-                                         bool may_send_base_page_url,
-                                         HandleSurroundingsCallback callback);
-
-  // Callback for GatherSurroundingTextWithCallback(). Will start the search
-  // term resolution request.
-  void StartSearchTermRequestFromSelection(
+  // Callback for GatherAndSaveSurroundingText, called when surrounding text is
+  // available.
+  void OnTextSurroundingSelectionAvailable(
       const base::string16& surrounding_text,
       int start_offset,
       int end_offset);
@@ -243,7 +227,8 @@
   IcingCallback icing_callback_;
 
   // Used to hold the context until an upcoming search term request is started.
-  std::unique_ptr<ContextualSearchContext> context_;
+  // Owned by the Java ContextualSearchContext.
+  ContextualSearchContext* context_;
 
   DISALLOW_COPY_AND_ASSIGN(ContextualSearchDelegate);
 };
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
index 5646a65a..d28dd01 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate_unittest.cc
@@ -95,10 +95,9 @@
     // ContextualSearchDelegate class takes ownership of the context.
     delegate_->set_context_for_testing(test_context_);
 
-    test_context_->start_offset = start_offset;
-    test_context_->end_offset = end_offset;
-    test_context_->surrounding_text = surrounding_text;
-    delegate_->ContinueSearchTermResolutionRequest();
+    test_context_->SetSelectionSurroundings(start_offset, end_offset,
+                                            surrounding_text);
+    delegate_->ResolveSearchTermFromContext();
     fetcher_ = test_factory_.GetFetcherByID(
         ContextualSearchDelegate::kContextualSearchURLFetcherID);
     ASSERT_TRUE(fetcher_);
@@ -147,9 +146,8 @@
                              int end_offset) {
     test_context_ = new ContextualSearchContext(
         "Bogus", std::string(), GURL(kSomeSpecificBasePage), "utf-8");
-    test_context_->surrounding_text = surrounding_text;
-    test_context_->start_offset = start_offset;
-    test_context_->end_offset = end_offset;
+    test_context_->SetSelectionSurroundings(start_offset, end_offset,
+                                            surrounding_text);
     // ContextualSearchDelegate class takes ownership of the context.
     delegate_->set_context_for_testing(test_context_);
   }
diff --git a/chrome/browser/android/contextualsearch/contextual_search_manager.cc b/chrome/browser/android/contextualsearch/contextual_search_manager.cc
index 9c96f5e..6eee9e4 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_manager.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_manager.cc
@@ -65,40 +65,32 @@
 void ContextualSearchManager::StartSearchTermResolutionRequest(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& j_selection,
-    const JavaParamRef<jstring>& j_home_country,
-    const JavaParamRef<jobject>& j_base_web_contents,
-    jboolean j_may_send_base_page_url) {
+    const base::android::JavaParamRef<jobject>& j_contextual_search_context,
+    const JavaParamRef<jobject>& j_base_web_contents) {
   WebContents* base_web_contents =
       WebContents::FromJavaWebContents(j_base_web_contents);
   DCHECK(base_web_contents);
-  std::string selection(
-      base::android::ConvertJavaStringToUTF8(env, j_selection));
-  std::string home_country(
-      base::android::ConvertJavaStringToUTF8(env, j_home_country));
-  bool may_send_base_page_url = j_may_send_base_page_url;
+  ContextualSearchContext* contextual_search_context =
+      ContextualSearchContext::FromJavaContextualSearchContext(
+          j_contextual_search_context);
   // Calls back to OnSearchTermResolutionResponse.
-  delegate_->StartSearchTermResolutionRequest(
-      selection, home_country, base_web_contents, may_send_base_page_url);
+  delegate_->StartSearchTermResolutionRequest(contextual_search_context,
+                                              base_web_contents);
 }
 
 void ContextualSearchManager::GatherSurroundingText(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& j_selection,
-    const JavaParamRef<jstring>& j_home_country,
-    const JavaParamRef<jobject>& j_base_web_contents,
-    jboolean j_may_send_base_page_url) {
+    const base::android::JavaParamRef<jobject>& j_contextual_search_context,
+    const JavaParamRef<jobject>& j_base_web_contents) {
   WebContents* base_web_contents =
       WebContents::FromJavaWebContents(j_base_web_contents);
   DCHECK(base_web_contents);
-  std::string selection(
-      base::android::ConvertJavaStringToUTF8(env, j_selection));
-  std::string home_country(
-      base::android::ConvertJavaStringToUTF8(env, j_home_country));
-  bool may_send_base_page_url = j_may_send_base_page_url;
-  delegate_->GatherAndSaveSurroundingText(
-      selection, home_country, base_web_contents, may_send_base_page_url);
+  ContextualSearchContext* contextual_search_context =
+      ContextualSearchContext::FromJavaContextualSearchContext(
+          j_contextual_search_context);
+  delegate_->GatherAndSaveSurroundingText(contextual_search_context,
+                                          base_web_contents);
 }
 
 base::android::ScopedJavaLocalRef<jstring>
diff --git a/chrome/browser/android/contextualsearch/contextual_search_manager.h b/chrome/browser/android/contextualsearch/contextual_search_manager.h
index f173376..1115509 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_manager.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_manager.h
@@ -33,25 +33,20 @@
   // content view core object).
   // Any outstanding server requests are canceled.
   // When the server responds with the search term, the Java object is notified
-  // by
-  // calling OnSearchTermResolutionResponse().
+  // by calling OnSearchTermResolutionResponse().
   void StartSearchTermResolutionRequest(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& j_selection,
-      const base::android::JavaParamRef<jstring>& j_home_country,
-      const base::android::JavaParamRef<jobject>& j_base_web_contents,
-      jboolean j_may_send_base_page_url);
+      const base::android::JavaParamRef<jobject>& j_contextual_search_context,
+      const base::android::JavaParamRef<jobject>& j_base_web_contents);
 
   // Gathers the surrounding text around the selection and saves it locally.
   // Does not send a search term resolution request to the server.
   void GatherSurroundingText(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& j_selection,
-      const base::android::JavaParamRef<jstring>& j_home_country,
-      const base::android::JavaParamRef<jobject>& j_base_web_contents,
-      jboolean j_may_send_base_page_url);
+      const base::android::JavaParamRef<jobject>& j_contextual_search_context,
+      const base::android::JavaParamRef<jobject>& j_base_web_contents);
 
   // Gets the target language for translation purposes.
   base::android::ScopedJavaLocalRef<jstring> GetTargetLanguage(
diff --git a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
index 446e96a..8e42f09 100644
--- a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
@@ -152,19 +152,9 @@
     if (!category.IsKnownCategory(KnownCategories::ARTICLES)) {
       return;
     }
-    switch (new_status) {
-      case CategoryStatus::AVAILABLE:
-      case CategoryStatus::AVAILABLE_LOADING:
-        break;  // nothing to do
-      case CategoryStatus::INITIALIZING:
-      case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED:
-      case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED:
-      case CategoryStatus::LOADING_ERROR:
-      case CategoryStatus::NOT_PROVIDED:
-      case CategoryStatus::SIGNED_OUT:
-        ContentSuggestionsNotificationHelper::HideAllNotifications(
-            CONTENT_SUGGESTIONS_HIDE_DISABLED);
-        break;
+    if (!ntp_snippets::IsCategoryStatusAvailable(new_status)) {
+      ContentSuggestionsNotificationHelper::HideAllNotifications(
+          CONTENT_SUGGESTIONS_HIDE_DISABLED);
     }
   }
 
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index b11c732a..cf07d9a0 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -46,7 +46,7 @@
 }
 
 namespace subresource_filter {
-class RulesetService;
+class ContentRulesetService;
 }
 
 namespace variations {
@@ -239,7 +239,7 @@
 
   // Returns the service providing versioned storage for rules used by the Safe
   // Browsing subresource filter.
-  virtual subresource_filter::RulesetService*
+  virtual subresource_filter::ContentRulesetService*
   subresource_filter_ruleset_service() = 0;
 
 #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index be34208..08f28a14 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -104,7 +104,7 @@
 #include "components/rappor/rappor_service_impl.h"
 #include "components/safe_json/safe_json_parser.h"
 #include "components/signin/core/common/profile_management_switches.h"
-#include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -903,7 +903,7 @@
   return NULL;
 }
 
-subresource_filter::RulesetService*
+subresource_filter::ContentRulesetService*
 BrowserProcessImpl::subresource_filter_ruleset_service() {
   DCHECK(CalledOnValidThread());
   if (!created_subresource_filter_ruleset_service_)
@@ -1224,11 +1224,12 @@
   base::FilePath indexed_ruleset_base_dir =
       user_data_dir.Append(subresource_filter::kTopLevelDirectoryName)
           .Append(subresource_filter::kIndexedRulesetBaseDirectoryName);
-  subresource_filter_ruleset_service_.reset(
-      new subresource_filter::RulesetService(
+  subresource_filter_ruleset_service_ =
+      base::MakeUnique<subresource_filter::ContentRulesetService>();
+  subresource_filter_ruleset_service_->set_ruleset_service(
+      base::MakeUnique<subresource_filter::RulesetService>(
           local_state(), blocking_task_runner,
-          base::MakeUnique<subresource_filter::ContentRulesetServiceDelegate>(),
-          indexed_ruleset_base_dir));
+          subresource_filter_ruleset_service_.get(), indexed_ruleset_base_dir));
 }
 
 void BrowserProcessImpl::CreateGCMDriver() {
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 167ce60e8..701e822 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -133,8 +133,8 @@
   safe_browsing::SafeBrowsingService* safe_browsing_service() override;
   safe_browsing::ClientSideDetectionService* safe_browsing_detection_service()
       override;
-  subresource_filter::RulesetService* subresource_filter_ruleset_service()
-      override;
+  subresource_filter::ContentRulesetService*
+  subresource_filter_ruleset_service() override;
 
 #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
   void StartAutoupdateTimer() override;
@@ -263,7 +263,7 @@
   scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_service_;
 
   bool created_subresource_filter_ruleset_service_;
-  std::unique_ptr<subresource_filter::RulesetService>
+  std::unique_ptr<subresource_filter::ContentRulesetService>
       subresource_filter_ruleset_service_;
 
   bool shutting_down_;
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 75a7370..1ee08dfe 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "ash/shell.h"
+#include "ash/sticky_keys/sticky_keys_controller.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
diff --git a/chrome/browser/chromeos/events/event_rewriter.cc b/chrome/browser/chromeos/events/event_rewriter.cc
index 5a68721..d622d52 100644
--- a/chrome/browser/chromeos/events/event_rewriter.cc
+++ b/chrome/browser/chromeos/events/event_rewriter.cc
@@ -8,9 +8,6 @@
 
 #include <vector>
 
-#include "ash/common/wm/window_state.h"
-#include "ash/sticky_keys/sticky_keys_controller.h"
-#include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -35,7 +32,6 @@
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
-#include "ui/wm/core/window_util.h"
 
 #if defined(USE_X11)
 #include <X11/extensions/XInput2.h>
@@ -306,7 +302,7 @@
 
 }  // namespace
 
-EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
+EventRewriter::EventRewriter(ui::EventRewriter* sticky_keys_controller)
     : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
       ime_keyboard_for_testing_(NULL),
       pref_service_for_testing_(NULL),
@@ -370,7 +366,7 @@
     // know that they don't have to be passed through the post-sticky key
     // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
     // because those phases do nothing with modifier key releases.
-    return sticky_keys_controller_->NextDispatchEvent(new_event);
+    return sticky_keys_controller_->NextDispatchEvent(last_event, new_event);
   }
   NOTREACHED();
   return ui::EVENT_REWRITE_CONTINUE;
@@ -523,8 +519,14 @@
   ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
   bool is_sticky_key_extension_command = false;
   if (sticky_keys_controller_) {
-    status = sticky_keys_controller_->RewriteKeyEvent(key_event, state.key_code,
-                                                      &state.flags);
+    auto tmp_event = key_event;
+    tmp_event.set_key_code(state.key_code);
+    tmp_event.set_flags(state.flags);
+    std::unique_ptr<ui::Event> output_event;
+    status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event);
+    if (status == ui::EVENT_REWRITE_REWRITTEN ||
+        status == ui::EVENT_REWRITE_DISPATCH_ANOTHER)
+      state.flags = output_event->flags();
     if (status == ui::EVENT_REWRITE_DISCARD)
       return ui::EVENT_REWRITE_DISCARD;
     is_sticky_key_extension_command =
@@ -573,8 +575,15 @@
   int flags = mouse_event.flags();
   RewriteLocatedEvent(mouse_event, &flags);
   ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
-  if (sticky_keys_controller_)
-    status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
+  if (sticky_keys_controller_) {
+    auto tmp_event = mouse_event;
+    tmp_event.set_flags(flags);
+    std::unique_ptr<ui::Event> output_event;
+    status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event);
+    if (status == ui::EVENT_REWRITE_REWRITTEN ||
+        status == ui::EVENT_REWRITE_DISPATCH_ANOTHER)
+      flags = output_event->flags();
+  }
   int changed_button = ui::EF_NONE;
   if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
       (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
@@ -608,21 +617,34 @@
     return ui::EVENT_REWRITE_CONTINUE;
   int flags = wheel_event.flags();
   RewriteLocatedEvent(wheel_event, &flags);
+  auto tmp_event = wheel_event;
+  tmp_event.set_flags(flags);
   ui::EventRewriteStatus status =
-      sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
-  if ((wheel_event.flags() == flags) &&
-      (status == ui::EVENT_REWRITE_CONTINUE)) {
-    return ui::EVENT_REWRITE_CONTINUE;
-  }
-  if (status == ui::EVENT_REWRITE_CONTINUE)
-    status = ui::EVENT_REWRITE_REWRITTEN;
-  ui::MouseWheelEvent* rewritten_wheel_event =
-      new ui::MouseWheelEvent(wheel_event);
-  rewritten_event->reset(rewritten_wheel_event);
-  rewritten_wheel_event->set_flags(flags);
+      sticky_keys_controller_->RewriteEvent(tmp_event, rewritten_event);
+
+  switch (status) {
+    case ui::EVENT_REWRITE_REWRITTEN:
+    case ui::EVENT_REWRITE_DISPATCH_ANOTHER:
+    // whell event has been rewritten and stored in |rewritten_event|.
 #if defined(USE_X11)
-  ui::UpdateX11EventForFlags(rewritten_wheel_event);
+      ui::UpdateX11EventForFlags(rewritten_event->get());
 #endif
+      break;
+    case ui::EVENT_REWRITE_CONTINUE:
+      if (flags != wheel_event.flags()) {
+        *rewritten_event = base::MakeUnique<ui::MouseWheelEvent>(wheel_event);
+        (*rewritten_event)->set_flags(flags);
+        status = ui::EVENT_REWRITE_REWRITTEN;
+#if defined(USE_X11)
+        ui::UpdateX11EventForFlags(rewritten_event->get());
+#endif
+      }
+      break;
+    case ui::EVENT_REWRITE_DISCARD:
+      NOTREACHED();
+      break;
+  }
+
   return status;
 }
 
@@ -645,17 +667,15 @@
 ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
     const ui::ScrollEvent& scroll_event,
     std::unique_ptr<ui::Event>* rewritten_event) {
-  int flags = scroll_event.flags();
-  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
-  if (sticky_keys_controller_)
-    status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
-  if (status == ui::EVENT_REWRITE_CONTINUE)
-    return status;
-  ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
-  rewritten_event->reset(rewritten_scroll_event);
-  rewritten_scroll_event->set_flags(flags);
+  if (!sticky_keys_controller_)
+    return ui::EVENT_REWRITE_CONTINUE;
+  ui::EventRewriteStatus status =
+      sticky_keys_controller_->RewriteEvent(scroll_event, rewritten_event);
+  // Scroll event shouldn't be discarded.
+  DCHECK_NE(status, ui::EVENT_REWRITE_DISCARD);
 #if defined(USE_X11)
-  ui::UpdateX11EventForFlags(rewritten_scroll_event);
+  if (status != ui::EVENT_REWRITE_CONTINUE)
+    ui::UpdateX11EventForFlags(rewritten_event->get());
 #endif
   return status;
 }
diff --git a/chrome/browser/chromeos/events/event_rewriter.h b/chrome/browser/chromeos/events/event_rewriter.h
index afb4d6f..e887a2c 100644
--- a/chrome/browser/chromeos/events/event_rewriter.h
+++ b/chrome/browser/chromeos/events/event_rewriter.h
@@ -19,10 +19,6 @@
 
 class PrefService;
 
-namespace ash {
-class StickyKeysController;
-}
-
 namespace ui {
 enum class DomCode;
 };
@@ -62,7 +58,7 @@
   // Does not take ownership of the |sticky_keys_controller|, which may also
   // be NULL (for testing without ash), in which case sticky key operations
   // don't happen.
-  explicit EventRewriter(ash::StickyKeysController* sticky_keys_controller);
+  explicit EventRewriter(ui::EventRewriter* sticky_keys_controller);
   ~EventRewriter() override;
 
   // Calls KeyboardDeviceAddedInternal.
@@ -179,7 +175,7 @@
 
   // The sticky keys controller is not owned here;
   // at time of writing it is a singleton in ash::Shell.
-  ash::StickyKeysController* sticky_keys_controller_;
+  ui::EventRewriter* const sticky_keys_controller_;
 
   // Some keyboard layouts have 'latching' keys, which either apply
   // a modifier while held down (like normal modifiers), or, if no
diff --git a/chrome/browser/component_updater/cros_component_installer.cc b/chrome/browser/component_updater/cros_component_installer.cc
index 0aa0886..04fb704 100644
--- a/chrome/browser/component_updater/cros_component_installer.cc
+++ b/chrome/browser/component_updater/cros_component_installer.cc
@@ -19,7 +19,30 @@
 namespace component_updater {
 
 #if defined(OS_CHROMEOS)
-void LogRegistrationResult(chromeos::DBusMethodCallStatus call_status,
+void LogLoadResult(chromeos::DBusMethodCallStatus call_status,
+                   const std::string& result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
+    DVLOG(1) << "Call to imageloader service failed.";
+    return;
+  }
+  if (result.empty()) {
+    DVLOG(1) << "Component load failed";
+    return;
+  }
+}
+void ImageLoaderLoad(const std::string& name) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  chromeos::ImageLoaderClient* loader =
+      chromeos::DBusThreadManager::Get()->GetImageLoaderClient();
+  if (loader) {
+    loader->LoadComponent(name, base::Bind(&LogLoadResult));
+  } else {
+    DVLOG(1) << "Failed to get ImageLoaderClient object.";
+  }
+}
+void LogRegistrationResult(const std::string& name,
+                           chromeos::DBusMethodCallStatus call_status,
                            bool result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
@@ -30,6 +53,7 @@
     DVLOG(1) << "Component registration failed";
     return;
   }
+  ImageLoaderLoad(name);
 }
 void ImageLoaderRegistration(const std::string& version,
                              const base::FilePath& install_dir,
@@ -40,7 +64,7 @@
 
   if (loader) {
     loader->RegisterComponent(name, version, install_dir.value(),
-                              base::Bind(&LogRegistrationResult));
+                              base::Bind(&LogRegistrationResult, name));
   } else {
     DVLOG(1) << "Failed to get ImageLoaderClient object.";
   }
diff --git a/chrome/browser/component_updater/subresource_filter_component_installer.cc b/chrome/browser/component_updater/subresource_filter_component_installer.cc
index ad4a450..a636e3fa 100644
--- a/chrome/browser/component_updater/subresource_filter_component_installer.cc
+++ b/chrome/browser/component_updater/subresource_filter_component_installer.cc
@@ -12,6 +12,7 @@
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "components/component_updater/component_updater_paths.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -79,10 +80,12 @@
       install_dir.Append(subresource_filter::kUnindexedRulesetDataFileName);
   ruleset_info.license_path =
       install_dir.Append(subresource_filter::kUnindexedRulesetLicenseFileName);
-  subresource_filter::RulesetService* ruleset_service =
+  subresource_filter::ContentRulesetService* content_ruleset_service =
       g_browser_process->subresource_filter_ruleset_service();
-  if (ruleset_service)
-    ruleset_service->IndexAndStoreAndPublishRulesetIfNeeded(ruleset_info);
+  if (content_ruleset_service) {
+    content_ruleset_service->IndexAndStoreAndPublishRulesetIfNeeded(
+        ruleset_info);
+  }
 }
 
 // Called during startup and installation before ComponentReady().
diff --git a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
index 2599c8c..810c57f 100644
--- a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/component_updater/mock_component_updater_service.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -39,13 +39,12 @@
  public:
   TestRulesetService(PrefService* local_state,
                      scoped_refptr<base::SequencedTaskRunner> task_runner,
+                     subresource_filter::ContentRulesetService* content_service,
                      const base::FilePath& base_dir)
-      : subresource_filter::RulesetService(
-            local_state,
-            task_runner,
-            base::MakeUnique<
-                subresource_filter::ContentRulesetServiceDelegate>(),
-            base_dir) {}
+      : subresource_filter::RulesetService(local_state,
+                                           task_runner,
+                                           content_service,
+                                           base_dir) {}
 
   ~TestRulesetService() override {}
 
@@ -103,19 +102,20 @@
     subresource_filter::IndexedRulesetVersion::RegisterPrefs(
         pref_service_.registry());
 
-    std::unique_ptr<subresource_filter::RulesetService> service(
-        new TestRulesetService(&pref_service_, task_runner_,
-                               ruleset_service_dir_.GetPath()));
+    auto content_service =
+        base::MakeUnique<subresource_filter::ContentRulesetService>();
+    auto test_ruleset_service = base::MakeUnique<TestRulesetService>(
+        &pref_service_, task_runner_, content_service.get(),
+        ruleset_service_dir_.GetPath());
+    test_ruleset_service_ = test_ruleset_service.get();
+    content_service->set_ruleset_service(std::move(test_ruleset_service));
 
-    TestingBrowserProcess::GetGlobal()->SetRulesetService(std::move(service));
+    TestingBrowserProcess::GetGlobal()->SetRulesetService(
+        std::move(content_service));
     traits_.reset(new SubresourceFilterComponentInstallerTraits());
   }
 
-  TestRulesetService* service() {
-    return static_cast<TestRulesetService*>(
-        TestingBrowserProcess::GetGlobal()
-            ->subresource_filter_ruleset_service());
-  }
+  TestRulesetService* service() { return test_ruleset_service_; }
 
   void WriteStringToFile(const std::string data, const base::FilePath& path) {
     ASSERT_EQ(static_cast<int32_t>(data.length()),
@@ -179,6 +179,8 @@
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   TestingPrefServiceSimple pref_service_;
 
+  TestRulesetService* test_ruleset_service_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterComponentInstallerTest);
 };
 
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 912e0ac..cbd6e335 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -3999,8 +3999,9 @@
                           result_id)));
 }
 
-#if defined(OS_WIN) || defined(OS_CHROMEOS)
+#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX)
 // This test is very flaky on Win XP and Aura. http://crbug.com/248438
+// Also flaky on Linux. http://crbug.com/700382
 #define MAYBE_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume \
     DISABLED_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume
 #else
diff --git a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
index f4e17f1..52d46aae 100644
--- a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
+++ b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
@@ -17,9 +17,11 @@
 #include <X11/Xlib.h>
 #include <X11/extensions/XTest.h>
 #include <X11/keysym.h>
-
+#include "ui/aura/window.h"
 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
 #include "ui/gfx/x/x11_types.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_observer_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -155,16 +157,33 @@
       incognito_browser, ui::VKEY_8, true, true, false, false));
 #elif defined(OS_LINUX) && defined(USE_X11)
   // Create an incognito browser to capture the focus.
-  CreateIncognitoBrowser();
+  Browser* incognito_browser = CreateIncognitoBrowser();
 
-  // On Linux, our infrastructure for sending keys just synthesize keyboard
-  // event and send them directly to the specified window, without notifying the
-  // X root window. It didn't work while testing global shortcut because the
-  // stuff of global shortcut on Linux need to be notified when KeyPress event
-  // is happening on X root window. So we simulate the keyboard input here.
-  SendNativeKeyEventToXDisplay(ui::VKEY_1, true, true, false);
-  SendNativeKeyEventToXDisplay(ui::VKEY_A, true, true, false);
-  SendNativeKeyEventToXDisplay(ui::VKEY_8, true, true, false);
+  views::DesktopWindowTreeHostX11* host =
+      static_cast<views::DesktopWindowTreeHostX11*>(
+          incognito_browser->window()->GetNativeWindow()->GetHost());
+
+  class GlobalCommandTreeHostObserver
+      : public views::DesktopWindowTreeHostObserverX11 {
+   public:
+    void OnWindowMapped(unsigned long xid) override {
+      // On Linux, our infrastructure for sending keys just synthesize keyboard
+      // event and send them directly to the specified window, without notifying
+      // the X root window. It didn't work while testing global shortcut because
+      // the stuff of global shortcut on Linux need to be notified when KeyPress
+      // event is happening on X root window. So we simulate the keyboard input
+      // here.
+      SendNativeKeyEventToXDisplay(ui::VKEY_1, true, true, false);
+      SendNativeKeyEventToXDisplay(ui::VKEY_A, true, true, false);
+      SendNativeKeyEventToXDisplay(ui::VKEY_8, true, true, false);
+    }
+
+    void OnWindowUnmapped(unsigned long xid) override {}
+  } observer;
+
+  // The observer sends the commands after window mapping
+  host->AddObserver(&observer);
+
 #elif defined(OS_MACOSX)
   // Create an incognito browser to capture the focus.
   CreateIncognitoBrowser();
@@ -179,6 +198,9 @@
   // but it might also be because the non-global shortcuts unexpectedly
   // worked.
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11)
+  host->RemoveObserver(&observer);
+#endif
 }
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc
index 16482e6e..7927b28a 100644
--- a/chrome/browser/first_run/try_chrome_dialog_view.cc
+++ b/chrome/browser/first_run/try_chrome_dialog_view.cc
@@ -34,6 +34,7 @@
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/layout/layout_constants.h"
+#include "ui/views/views_delegate.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -134,7 +135,9 @@
   columns->AddPaddingColumn(0, icon_size.width());
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0,
                      views::GridLayout::USE_PREF, 0, 0);
-  columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
+  columns->AddPaddingColumn(
+      0, views::ViewsDelegate::GetInstance()->GetDistanceMetric(
+             views::DistanceMetric::RELATED_BUTTON_HORIZONTAL));
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0,
                      views::GridLayout::USE_PREF, 0, 0);
 
diff --git a/chrome/browser/notifications/notification_permission_context.cc b/chrome/browser/notifications/notification_permission_context.cc
index 36c6d91..3d3f0830 100644
--- a/chrome/browser/notifications/notification_permission_context.cc
+++ b/chrome/browser/notifications/notification_permission_context.cc
@@ -4,13 +4,13 @@
 
 #include "chrome/browser/notifications/notification_permission_context.h"
 
-#include <algorithm>
 #include <deque>
 
 #include "base/callback.h"
 #include "base/location.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
@@ -122,10 +122,7 @@
 void VisibilityTimerTabHelper::CancelTask(const PermissionRequestID& id) {
   bool deleting_front = task_queue_.front().id == id;
 
-  task_queue_.erase(
-      std::remove_if(task_queue_.begin(), task_queue_.end(),
-                     [id](const Task& task) { return task.id == id; }),
-      task_queue_.end());
+  base::EraseIf(task_queue_, [id](const Task& task) { return task.id == id; });
 
   if (!task_queue_.empty() && is_visible_ && deleting_front)
     task_queue_.front().timer->Reset();
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
index 932098d..0b0b51e 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
@@ -76,9 +76,6 @@
     case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED:
       os << "CATEGORY_EXPLICITLY_DISABLED";
       break;
-    case CategoryStatus::SIGNED_OUT:
-      os << "SIGNED_OUT";
-      break;
     case CategoryStatus::LOADING_ERROR:
       os << "LOADING_ERROR";
       break;
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 5dc721b6..16fea3c 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -100,6 +100,10 @@
   registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
                                 false);
   registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutGroupSelected, false);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingSawInterstitialExtendedReporting, false);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
   registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
                                 false);
   registry->RegisterBooleanPref(prefs::kSSLErrorOverrideAllowed, true);
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.css b/chrome/browser/resources/vr_shell/vr_shell_ui.css
index d0242ae..c066e2b6 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.css
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.css
@@ -145,6 +145,12 @@
   background-color: pink;
 }
 
+#content-interceptor {
+  height: 1px;
+  opacity: 0;
+  width: 1px;
+}
+
 .tab {
   background-color: #eee;
   border-radius: 6px;
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.html b/chrome/browser/resources/vr_shell/vr_shell_ui.html
index c2839d1..d522e96 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.html
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.html
@@ -73,6 +73,7 @@
       $i18n{forward}
     </div>
     <div id="reload-ui-button" class="ui-element">Reload UI</div>
+    <div id="content-interceptor" class="ui-element"></div>
     <div id="tab-template" class="tab"></div>
     <!--The tab container element behaves like a scroll view (in conjunction
         with the clip view). -->
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js
index aa5dde6..098f5b8 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.js
@@ -39,6 +39,8 @@
       /** @const */ this.MENU_MODE_SCREEN_HEIGHT = 0.8;
       /** @const */ this.MENU_MODE_SCREEN_ELEVATION = 0.2;
       /** @const */ this.BACKGROUND_DISTANCE_MULTIPLIER = 1.414;
+      /** @const */ this.DOM_INTERCEPTOR_SELECTOR = '#content-interceptor';
+      /** @const */ this.DOM_INTERCEPTOR_ELEVATION = 0.01;
 
       this.menuMode = false;
       this.fullscreen = false;
@@ -61,6 +63,24 @@
       backPlane.setFill(new api.NoFill());
       ui.addElement(backPlane);
 
+      // Place invisible plane on top of content quad, to intercept the clicks
+      // while on menu mode.
+      this.interceptor = new DomUiElement(this.DOM_INTERCEPTOR_SELECTOR);
+      let update = new api.UiElementUpdate();
+      update.setTranslation(0, 0, this.DOM_INTERCEPTOR_ELEVATION);
+      update.setVisible(false);
+      update.setParentId(this.elementId);
+      update.setSize(
+          this.MENU_MODE_SCREEN_HEIGHT * this.SCREEN_RATIO,
+          this.MENU_MODE_SCREEN_HEIGHT);
+      ui.updateElement(this.interceptor.id, update);
+      let interceptorButton =
+          document.querySelector(this.DOM_INTERCEPTOR_SELECTOR);
+      interceptorButton.addEventListener('click', function() {
+        uiManager.exitMenuMode();
+        ui.flush();
+      });
+
       this.updateState();
     }
 
@@ -106,6 +126,10 @@
         distance = this.FULLSCREEN_DISTANCE;
       }
 
+      let update = new api.UiElementUpdate();
+      update.setVisible(this.menuMode);
+      ui.updateElement(this.interceptor.id, update);
+
       let anim;
       anim = new api.Animation(this.elementId, ANIM_DURATION);
       anim.setTranslation(0, y, -distance);
@@ -999,6 +1023,13 @@
       this.controls.setForwardButtonEnabled(this.canGoForward);
     }
 
+    exitMenuMode() {
+      if (this.menuMode) {
+        this.menuMode = false;
+        this.updateState();
+      }
+    }
+
     updateState() {
       /** @const */ var URL_INDICATOR_VISIBILITY_TIMEOUT_MS = 5000;
 
diff --git a/chrome/browser/subresource_filter/test_ruleset_publisher.cc b/chrome/browser/subresource_filter/test_ruleset_publisher.cc
index ea9c817..c5d3cfd 100644
--- a/chrome/browser/subresource_filter/test_ruleset_publisher.cc
+++ b/chrome/browser/subresource_filter/test_ruleset_publisher.cc
@@ -8,7 +8,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/browser_process.h"
-#include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,20 +20,20 @@
 class RulesetDistributionListener {
  public:
   RulesetDistributionListener()
-      : delegate_(static_cast<ContentRulesetServiceDelegate*>(
-            g_browser_process->subresource_filter_ruleset_service()
-                ->delegate())) {
-    delegate_->SetRulesetPublishedCallbackForTesting(run_loop_.QuitClosure());
+      : content_service_(
+            g_browser_process->subresource_filter_ruleset_service()) {
+    content_service_->SetRulesetPublishedCallbackForTesting(
+        run_loop_.QuitClosure());
   }
 
   ~RulesetDistributionListener() {
-    delegate_->SetRulesetPublishedCallbackForTesting(base::Closure());
+    content_service_->SetRulesetPublishedCallbackForTesting(base::Closure());
   }
 
   void AwaitDistribution() { run_loop_.Run(); }
 
  private:
-  ContentRulesetServiceDelegate* delegate_;
+  ContentRulesetService* content_service_;
   base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(RulesetDistributionListener);
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.cc b/chrome/browser/supervised_user/supervised_user_interstitial.cc
index 5958869c..d150be1 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.cc
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.cc
@@ -267,6 +267,7 @@
     std::string message = l10n_util::GetStringFUTF8(
         IDS_BLOCK_INTERSTITIAL_DEFAULT_FEEDBACK_TEXT, reason);
 #if defined(OS_ANDROID)
+    DCHECK(profile_->IsChild());
     ReportChildAccountFeedback(web_contents_, message, url_);
 #else
     chrome::ShowFeedbackPage(chrome::FindBrowserWithWebContents(web_contents_),
diff --git a/chrome/browser/ui/libgtkui/select_file_dialog_interactive_uitest.cc b/chrome/browser/ui/libgtkui/select_file_dialog_interactive_uitest.cc
index be18ef84d2..669974c2e 100644
--- a/chrome/browser/ui/libgtkui/select_file_dialog_interactive_uitest.cc
+++ b/chrome/browser/ui/libgtkui/select_file_dialog_interactive_uitest.cc
@@ -68,7 +68,8 @@
 }  // namespace libgtkui
 
 // Leaks in GtkFileChooserDialog. http://crbug.com/537468
-#if defined(ADDRESS_SANITIZER)
+// Flaky on Linux. http://crbug.com/700134
+#if defined(ADDRESS_SANITIZER) || defined(OS_LINUX)
 #define MAYBE_ModalTest DISABLED_ModalTest
 #else
 #define MAYBE_ModalTest ModalTest
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
index 2abe3d7..ef959af 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc
@@ -17,6 +17,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/controls/tree/tree_view.h"
+#include "ui/views/test/test_views_delegate.h"
 
 using base::ASCIIToUTF16;
 using base::UTF8ToUTF16;
@@ -112,6 +113,7 @@
   content::TestBrowserThreadBundle thread_bundle_;
 
   BookmarkModel* model_;
+  views::TestViewsDelegate views_delegate_;
   std::unique_ptr<TestingProfile> profile_;
 
  private:
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc
index fef73a45..20793d9 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -57,17 +57,32 @@
   return profile->GetPrefs();
 }
 
+ChromeViewsDelegate* views_delegate = nullptr;
+
 }  // namespace
 
 
 // ChromeViewsDelegate --------------------------------------------------------
 
-#if !defined(OS_WIN)
-ChromeViewsDelegate::ChromeViewsDelegate() {}
+#if defined(OS_WIN)
+ChromeViewsDelegate::ChromeViewsDelegate()
+    : in_autohide_edges_callback_(false), weak_factory_(this) {
+#else
+ChromeViewsDelegate::ChromeViewsDelegate() {
 #endif
+  DCHECK(!views_delegate);
+  views_delegate = this;
+}
 
 ChromeViewsDelegate::~ChromeViewsDelegate() {
   DCHECK_EQ(0u, ref_count_);
+
+  DCHECK_EQ(this, views_delegate);
+  views_delegate = nullptr;
+}
+
+ChromeViewsDelegate* ChromeViewsDelegate::GetInstance() {
+  return views_delegate;
 }
 
 void ChromeViewsDelegate::SaveWindowPlacement(const views::Widget* window,
@@ -196,58 +211,61 @@
   return content::BrowserThread::GetBlockingPool();
 }
 
-gfx::Insets ChromeViewsDelegate::GetDialogButtonInsets() const {
+gfx::Insets ChromeViewsDelegate::GetInsetsMetric(
+    views::InsetsMetric metric) const {
   const LayoutDelegate* layout_delegate = LayoutDelegate::Get();
-  const int top = layout_delegate->GetMetric(
-      LayoutDelegate::Metric::DIALOG_BUTTON_TOP_SPACING);
-  const int margin = layout_delegate->GetMetric(
-      LayoutDelegate::Metric::DIALOG_BUTTON_MARGIN);
-  return gfx::Insets(top, margin, margin, margin);
+  switch (metric) {
+    case views::InsetsMetric::DIALOG_BUTTON: {
+      const int top = layout_delegate->GetMetric(
+          LayoutDelegate::Metric::DIALOG_BUTTON_TOP_SPACING);
+      const int margin = layout_delegate->GetMetric(
+          LayoutDelegate::Metric::DIALOG_BUTTON_MARGIN);
+      return gfx::Insets(top, margin, margin, margin);
+    }
+    case views::InsetsMetric::DIALOG_FRAME_VIEW: {
+      const int top = layout_delegate->GetMetric(
+          LayoutDelegate::Metric::PANEL_CONTENT_MARGIN);
+      const int side = layout_delegate->GetMetric(
+          LayoutDelegate::Metric::DIALOG_BUTTON_MARGIN);
+      // Titles are inset at the top and sides, but not at the bottom.
+      return gfx::Insets(top, side, 0, side);
+    }
+    case views::InsetsMetric::BUBBLE_DIALOG:
+      return gfx::Insets(layout_delegate->GetMetric(
+          LayoutDelegate::Metric::PANEL_CONTENT_MARGIN));
+  }
+  NOTREACHED();
+  return gfx::Insets();
 }
 
-int ChromeViewsDelegate::GetDialogCloseButtonMargin() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::DIALOG_CLOSE_BUTTON_MARGIN);
+int ChromeViewsDelegate::GetDistanceMetric(views::DistanceMetric metric) const {
+  switch (metric) {
+    case views::DistanceMetric::CLOSE_BUTTON_MARGIN:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::DIALOG_CLOSE_BUTTON_MARGIN);
+    case views::DistanceMetric::RELATED_BUTTON_HORIZONTAL:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::RELATED_BUTTON_HORIZONTAL_SPACING);
+    case views::DistanceMetric::RELATED_CONTROL_HORIZONTAL:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::RELATED_CONTROL_HORIZONTAL_SPACING);
+    case views::DistanceMetric::RELATED_CONTROL_VERTICAL:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::RELATED_CONTROL_VERTICAL_SPACING);
+    case views::DistanceMetric::DIALOG_BUTTON_MINIMUM_WIDTH:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::DIALOG_BUTTON_MINIMUM_WIDTH);
+    case views::DistanceMetric::BUTTON_HORIZONTAL_PADDING:
+      return LayoutDelegate::Get()->GetMetric(
+          LayoutDelegate::Metric::BUTTON_HORIZONTAL_PADDING);
+  }
+  NOTREACHED();
+  return 0;
 }
 
-int ChromeViewsDelegate::GetDialogRelatedButtonHorizontalSpacing() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::RELATED_BUTTON_HORIZONTAL_SPACING);
-}
-
-int ChromeViewsDelegate::GetDialogRelatedControlVerticalSpacing() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::RELATED_CONTROL_VERTICAL_SPACING);
-}
-
-gfx::Insets ChromeViewsDelegate::GetDialogFrameViewInsets() const {
-  const LayoutDelegate* layout_delegate = LayoutDelegate::Get();
-  const int top = layout_delegate->GetMetric(
-      LayoutDelegate::Metric::PANEL_CONTENT_MARGIN);
-  const int side = layout_delegate->GetMetric(
-      LayoutDelegate::Metric::DIALOG_BUTTON_MARGIN);
-  // Titles are inset at the top and sides, but not at the bottom.
-  return gfx::Insets(top, side, 0, side);
-}
-
-gfx::Insets ChromeViewsDelegate::GetBubbleDialogMargins() const {
-  return gfx::Insets(LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::PANEL_CONTENT_MARGIN));
-}
-
-int ChromeViewsDelegate::GetButtonMinimumWidth() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::BUTTON_MINIMUM_WIDTH);
-}
-
-int ChromeViewsDelegate::GetDialogButtonMinimumWidth() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::DIALOG_BUTTON_MINIMUM_WIDTH);
-}
-
-int ChromeViewsDelegate::GetButtonHorizontalPadding() const {
-  return LayoutDelegate::Get()->GetMetric(
-      LayoutDelegate::Metric::BUTTON_HORIZONTAL_PADDING);
+int ChromeViewsDelegate::GetDefaultDistanceMetric(
+    views::DistanceMetric metric) const {
+  return views::ViewsDelegate::GetDistanceMetric(metric);
 }
 
 #if !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index 5e0bc98..4b9c34e2 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -20,6 +20,8 @@
   ChromeViewsDelegate();
   ~ChromeViewsDelegate() override;
 
+  static ChromeViewsDelegate* GetInstance();
+
   // views::ViewsDelegate:
   void SaveWindowPlacement(const views::Widget* window,
                            const std::string& window_name,
@@ -58,15 +60,10 @@
   std::string GetApplicationName() override;
   scoped_refptr<base::TaskRunner> GetBlockingPoolTaskRunner() override;
 
-  gfx::Insets GetDialogButtonInsets() const override;
-  int GetDialogCloseButtonMargin() const override;
-  int GetDialogRelatedButtonHorizontalSpacing() const override;
-  int GetDialogRelatedControlVerticalSpacing() const override;
-  gfx::Insets GetDialogFrameViewInsets() const override;
-  gfx::Insets GetBubbleDialogMargins() const override;
-  int GetButtonMinimumWidth() const override;
-  int GetDialogButtonMinimumWidth() const override;
-  int GetButtonHorizontalPadding() const override;
+  gfx::Insets GetInsetsMetric(views::InsetsMetric metric) const override;
+  int GetDistanceMetric(views::DistanceMetric metric) const override;
+
+  int GetDefaultDistanceMetric(views::DistanceMetric metric) const;
 
  private:
 #if defined(OS_WIN)
diff --git a/chrome/browser/ui/views/chrome_views_delegate_win.cc b/chrome/browser/ui/views/chrome_views_delegate_win.cc
index 342e139..3f87f6f 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_win.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_win.cc
@@ -106,9 +106,6 @@
 
 }  // namespace
 
-ChromeViewsDelegate::ChromeViewsDelegate()
-    : in_autohide_edges_callback_(false), weak_factory_(this) {}
-
 HICON ChromeViewsDelegate::GetDefaultWindowIcon() const {
   return GetAppIcon();
 }
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views_unittest.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views_unittest.cc
index d14182f9..4853d07 100644
--- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views_unittest.cc
+++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/storage_monitor/storage_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/button/checkbox.h"
+#include "ui/views/test/test_views_delegate.h"
 
 using ::testing::_;
 using ::testing::AnyNumber;
@@ -60,6 +61,7 @@
  private:
   // TODO(gbillock): Get rid of this mock; make something specialized.
   NiceMock<MediaGalleriesDialogControllerMock> controller_;
+  views::TestViewsDelegate views_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaGalleriesDialogTest);
 };
diff --git a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
index 1ea3979..4c2c545 100644
--- a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
@@ -16,6 +16,7 @@
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/test/test_views_delegate.h"
 
 using ::testing::_;
 using ::testing::Return;
@@ -87,6 +88,7 @@
             mock_global_error_with_standard_bubble_->AsWeakPtr())) {}
 
  protected:
+  views::TestViewsDelegate views_delegate_;
   std::unique_ptr<StrictMock<MockGlobalErrorWithStandardBubble>>
       mock_global_error_with_standard_bubble_;
   views::View arg_view_;
diff --git a/chrome/browser/ui/views/harmony/layout_delegate.cc b/chrome/browser/ui/views/harmony/layout_delegate.cc
index 4ecca84..79d88b02 100644
--- a/chrome/browser/ui/views/harmony/layout_delegate.cc
+++ b/chrome/browser/ui/views/harmony/layout_delegate.cc
@@ -6,6 +6,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "chrome/browser/ui/views/chrome_views_delegate.h"
 #include "chrome/browser/ui/views/harmony/harmony_layout_delegate.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/views/layout/layout_constants.h"
@@ -23,7 +24,8 @@
 int LayoutDelegate::GetMetric(Metric metric) const {
   switch (metric) {
     case Metric::BUTTON_HORIZONTAL_PADDING:
-      return views::kButtonHorizontalPadding;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::BUTTON_HORIZONTAL_PADDING);
     case Metric::BUTTON_MAX_LINKABLE_WIDTH:
       return 0;  // Buttons never expand during layout (add padding instead).
     case Metric::BUTTON_MINIMUM_WIDTH:
@@ -31,19 +33,24 @@
     case Metric::DIALOG_BUTTON_MARGIN:
       return views::kButtonHEdgeMarginNew;
     case Metric::DIALOG_BUTTON_MINIMUM_WIDTH:
-      return views::kDialogMinimumButtonWidth;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::DIALOG_BUTTON_MINIMUM_WIDTH);
     case Metric::DIALOG_BUTTON_TOP_SPACING:
       return 0;
     case Metric::DIALOG_CLOSE_BUTTON_MARGIN:
-      return views::kCloseButtonMargin;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::CLOSE_BUTTON_MARGIN);
     case Metric::PANEL_CONTENT_MARGIN:
       return views::kPanelHorizMargin;
     case Metric::RELATED_BUTTON_HORIZONTAL_SPACING:
-      return views::kRelatedButtonHSpacing;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::RELATED_BUTTON_HORIZONTAL);
     case Metric::RELATED_CONTROL_HORIZONTAL_SPACING:
-      return views::kRelatedControlHorizontalSpacing;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::RELATED_CONTROL_HORIZONTAL);
     case Metric::RELATED_CONTROL_VERTICAL_SPACING:
-      return views::kRelatedControlVerticalSpacing;
+      return ChromeViewsDelegate::GetInstance()->GetDefaultDistanceMetric(
+          views::DistanceMetric::RELATED_CONTROL_VERTICAL);
     case Metric::RELATED_LABEL_HORIZONTAL_SPACING:
       return views::kItemLabelSpacing;
     case Metric::SUBSECTION_HORIZONTAL_INDENT:
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc
index 5bb18d38..6baa1e9 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc
@@ -26,6 +26,7 @@
 #include "content/public/common/content_features.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/views/test/widget_test.h"
 
 using testing::Eq;
 using testing::Field;
@@ -332,7 +333,12 @@
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(focused_window));
   content::RunAllPendingInMessageLoop();
 
-  EXPECT_FALSE(browser()->window()->IsActive());
+  gfx::NativeWindow window = browser()->window()->GetNativeWindow();
+  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+  ASSERT_NE(nullptr, widget);
+
+  views::test::WidgetActivationWaiter inactive_waiter(widget, false);
+  inactive_waiter.Wait();
   ManagePasswordsBubbleView::set_auto_signin_toast_timeout(0);
   SetupAutoSignin(std::move(local_credentials));
   content::RunAllPendingInMessageLoop();
@@ -342,6 +348,7 @@
   focused_window->window()->Close();
   browser()->window()->Activate();
   content::RunAllPendingInMessageLoop();
-  EXPECT_TRUE(browser()->window()->IsActive());
+  views::test::WidgetActivationWaiter active_waiter(widget, true);
+  active_waiter.Wait();
   EXPECT_FALSE(IsBubbleShowing());
 }
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
index 18bfd29b..b95b6ada 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/ssl_client_auth_requestor_mock.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/ssl_client_certificate_selector.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -27,6 +28,7 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/widget_test.h"
 
 #if defined(USE_NSS_CERTS)
 #include "crypto/scoped_test_nss_db.h"
@@ -261,6 +263,12 @@
     selector_1_->Init();
     selector_1_->Show();
 
+    gfx::NativeWindow window = browser_1_->window()->GetNativeWindow();
+    views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+    ASSERT_NE(nullptr, widget);
+    views::test::WidgetActivationWaiter waiter(widget, true);
+    waiter.Wait();
+
     EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
   }
 
diff --git a/chrome/browser/ui/views/toolbar/reload_button_unittest.cc b/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
index fe70511..00195421 100644
--- a/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/run_loop.h"
 #include "chrome/browser/ui/views/toolbar/reload_button.h"
+
+#include "base/run_loop.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event_utils.h"
+#include "ui/views/test/test_views_delegate.h"
 
 class ReloadButtonTest : public ChromeRenderViewHostTestHarness {
  public:
@@ -26,7 +28,13 @@
   int reload_count() { return reload_.testing_reload_count_; }
 
  protected:
+  ReloadButton* reload() { return &reload_; }
+
+ private:
+  views::TestViewsDelegate views_delegate_;
   ReloadButton reload_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReloadButtonTest);
 };
 
 ReloadButtonTest::ReloadButtonTest() : reload_(profile(), nullptr) {
@@ -59,18 +67,18 @@
   // Press the button.  This should start the double-click timer.
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  reload_.ButtonPressed(&reload_, e);
+  reload()->ButtonPressed(reload(), e);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, true,
              false);
 
   // Now change the mode (as if the browser had started loading the page).  This
   // should cancel the double-click timer since the button is not hovered.
-  reload_.ChangeMode(ReloadButton::MODE_STOP, false);
+  reload()->ChangeMode(ReloadButton::MODE_STOP, false);
   CheckState(true, ReloadButton::MODE_STOP, ReloadButton::MODE_STOP, false,
              false);
 
   // Press the button again.  This should change back to reload.
-  reload_.ButtonPressed(&reload_, e);
+  reload()->ButtonPressed(reload(), e);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false,
              false);
 }
@@ -79,12 +87,12 @@
   // Start by pressing the button.
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  reload_.ButtonPressed(&reload_, e);
+  reload()->ButtonPressed(reload(), e);
 
   // Try to press the button again.  This should do nothing because the timer is
   // running.
   int original_reload_count = reload_count();
-  reload_.ButtonPressed(&reload_, e);
+  reload()->ButtonPressed(reload(), e);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, true,
              false);
   EXPECT_EQ(original_reload_count, reload_count());
@@ -92,7 +100,7 @@
   // Hover the button, and change mode.  The visible mode should not change,
   // again because the timer is running.
   set_mouse_hovered(true);
-  reload_.ChangeMode(ReloadButton::MODE_STOP, false);
+  reload()->ChangeMode(ReloadButton::MODE_STOP, false);
   CheckState(true, ReloadButton::MODE_STOP, ReloadButton::MODE_RELOAD, true,
              false);
 
@@ -106,13 +114,13 @@
   // Change to stop and hover.
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  reload_.ButtonPressed(&reload_, e);
-  reload_.ChangeMode(ReloadButton::MODE_STOP, false);
+  reload()->ButtonPressed(reload(), e);
+  reload()->ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
 
   // Now change back to reload.  This should result in a disabled stop button
   // due to the hover.
-  reload_.ChangeMode(ReloadButton::MODE_RELOAD, false);
+  reload()->ChangeMode(ReloadButton::MODE_RELOAD, false);
   CheckState(false, ReloadButton::MODE_RELOAD, ReloadButton::MODE_STOP, false,
              true);
 
@@ -120,7 +128,7 @@
   set_mouse_hovered(false);
   ui::MouseEvent e2(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
                     ui::EventTimeForNow(), 0, 0);
-  reload_.OnMouseExited(e2);
+  reload()->OnMouseExited(e2);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false,
              false);
 }
@@ -129,13 +137,13 @@
   // Change to stop and hover.
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  reload_.ButtonPressed(&reload_, e);
-  reload_.ChangeMode(ReloadButton::MODE_STOP, false);
+  reload()->ButtonPressed(reload(), e);
+  reload()->ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
 
   // Press the button.  This should change back to reload despite the hover,
   // because it's a direct user action.
-  reload_.ButtonPressed(&reload_, e);
+  reload()->ButtonPressed(reload(), e);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false,
              false);
 }
@@ -144,10 +152,10 @@
   // Change to stop, hover, and change back to reload.
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  reload_.ButtonPressed(&reload_, e);
-  reload_.ChangeMode(ReloadButton::MODE_STOP, false);
+  reload()->ButtonPressed(reload(), e);
+  reload()->ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
-  reload_.ChangeMode(ReloadButton::MODE_RELOAD, false);
+  reload()->ChangeMode(ReloadButton::MODE_RELOAD, false);
 
   // Now fire the stop-to-reload timer.  This should reset the button.
   base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/views/website_settings/permission_selector_row.cc b/chrome/browser/ui/views/website_settings/permission_selector_row.cc
index 6554dae..7fca592 100644
--- a/chrome/browser/ui/views/website_settings/permission_selector_row.cc
+++ b/chrome/browser/ui/views/website_settings/permission_selector_row.cc
@@ -26,11 +26,6 @@
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-namespace {
-// Minimum distance between the label and its corresponding menu.
-const int kMinSeparationBetweenLabelAndMenu = 16;
-}  // namespace
-
 namespace internal {
 
 // The |PermissionMenuButton| provides a menu for selecting a setting a
@@ -74,6 +69,12 @@
                                            PermissionMenuModel* model,
                                            bool show_menu_marker)
     : MenuButton(text, this, show_menu_marker), menu_model_(model) {
+  // Since PermissionMenuButtons are added to a GridLayout, they are not always
+  // sized to their preferred size. Disclosure arrows are always right-aligned,
+  // so if the text is not right-aligned, awkward space appears between the text
+  // and the arrow.
+  SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+
   // Update the themed border before the NativeTheme is applied. Usually this
   // happens in a call to LabelButton::OnNativeThemeChanged(). However, if
   // PermissionMenuButton called that from its override, the NativeTheme would
@@ -232,39 +233,19 @@
 PermissionSelectorRow::PermissionSelectorRow(
     Profile* profile,
     const GURL& url,
-    const WebsiteSettingsUI::PermissionInfo& permission)
+    const WebsiteSettingsUI::PermissionInfo& permission,
+    views::GridLayout* layout)
     : profile_(profile), icon_(NULL), menu_button_(NULL), combobox_(NULL) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
-  const int column_set_id = 0;
-  views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
-  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
-                        views::GridLayout::FIXED, kPermissionIconColumnWidth,
-                        0);
-  column_set->AddPaddingColumn(0, kPermissionIconMarginLeft);
-  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
-                        views::GridLayout::USE_PREF, 0, 0);
-  column_set->AddPaddingColumn(1, kMinSeparationBetweenLabelAndMenu);
-  column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 0,
-                        views::GridLayout::USE_PREF, 0, 0);
-
-  layout->StartRow(1, column_set_id);
   // Create the permission icon.
   icon_ = new NonAccessibleImageView();
   const gfx::Image& image = WebsiteSettingsUI::GetPermissionIcon(permission);
   icon_->SetImage(image.ToImageSkia());
-  layout->AddView(icon_,
-                  1,
-                  1,
-                  views::GridLayout::CENTER,
+  layout->AddView(icon_, 1, 1, views::GridLayout::CENTER,
                   views::GridLayout::CENTER);
   // Create the label that displays the permission type.
-  views::Label* label = new views::Label(
+  label_ = new views::Label(
       WebsiteSettingsUI::PermissionTypeToUIString(permission.type));
-  layout->AddView(label,
-                  1,
-                  1,
-                  views::GridLayout::LEADING,
+  layout->AddView(label_, 1, 1, views::GridLayout::LEADING,
                   views::GridLayout::CENTER);
   // Create the menu model.
   menu_model_.reset(new PermissionMenuModel(
@@ -290,10 +271,6 @@
   observer_list_.AddObserver(observer);
 }
 
-void PermissionSelectorRow::ChildPreferredSizeChanged(View* child) {
-  Layout();
-}
-
 PermissionSelectorRow::~PermissionSelectorRow() {
   // Gross. On paper the Combobox and the ComboboxModelAdapter are both owned by
   // this class, but actually, the Combobox is owned by View and will be
@@ -306,7 +283,7 @@
   // Technically, the MenuButton has the same problem, but MenuButton doesn't
   // use its model in its destructor.
   if (combobox_)
-    RemoveChildView(combobox_);
+    combobox_->parent()->RemoveChildView(combobox_);
 }
 
 void PermissionSelectorRow::InitializeMenuButtonView(
@@ -352,6 +329,10 @@
         profile_, permission.type, permission.setting,
         permission.default_setting, content_settings::SETTING_SOURCE_USER));
     menu_button_->SizeToPreferredSize();
+    // Re-layout will be done at the |WebsiteSettingsPopupView| level, since
+    // that view may need to resize itself to accomodate the new sizes of its
+    // contents.
+    menu_button_->InvalidateLayout();
   } else if (combobox_) {
     bool use_default = permission.setting == CONTENT_SETTING_DEFAULT;
     combobox_->UpdateSelectedIndex(use_default);
@@ -360,3 +341,11 @@
   for (PermissionSelectorRowObserver& observer : observer_list_)
     observer.OnPermissionChanged(permission);
 }
+
+views::View* PermissionSelectorRow::button() {
+  // These casts are required because the two arms of a ?: cannot have different
+  // types T1 and T2, even if the resulting value of the ?: is about to be a T
+  // and T1 and T2 are both subtypes of T.
+  return menu_button_ ? static_cast<views::View*>(menu_button_)
+                      : static_cast<views::View*>(combobox_);
+}
diff --git a/chrome/browser/ui/views/website_settings/permission_selector_row.h b/chrome/browser/ui/views/website_settings/permission_selector_row.h
index 28310c39..51a9b62 100644
--- a/chrome/browser/ui/views/website_settings/permission_selector_row.h
+++ b/chrome/browser/ui/views/website_settings/permission_selector_row.h
@@ -10,14 +10,12 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "base/strings/string16.h"
 #include "chrome/browser/ui/views/website_settings/permission_selector_row_observer.h"
 #include "chrome/browser/ui/website_settings/permission_menu_model.h"
 #include "chrome/browser/ui/website_settings/website_settings_ui.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "ui/views/controls/button/menu_button_listener.h"
-#include "ui/views/view.h"
 
 class Profile;
 
@@ -27,30 +25,37 @@
 class PermissionMenuButton;
 }
 
+namespace test {
+class WebsiteSettingsPopupViewTestApi;
+}
+
 namespace views {
 class GridLayout;
 class ImageView;
+class Label;
+class View;
 }
 
-// A PermissionSelectorRow is a row in the Page Info bubble that shows a
+// A |PermissionSelectorRow| is a row in the Page Info bubble that shows a
 // permission that a site can have ambient access to, and allows the user to
-// control whether that access is granted.
-class PermissionSelectorRow : public views::View {
+// control whether that access is granted. A |PermissionSelectorRow| is not
+// itself a |View|, but creates some |View|s, which end up owned by the |View|
+// hierarchy.
+class PermissionSelectorRow {
  public:
+  // The |PermissionSelectorRow|'s constituent views are added to |layout|.
   PermissionSelectorRow(Profile* profile,
                         const GURL& url,
-                        const WebsiteSettingsUI::PermissionInfo& permission);
+                        const WebsiteSettingsUI::PermissionInfo& permission,
+                        views::GridLayout* layout);
+  virtual ~PermissionSelectorRow();
 
   void AddObserver(PermissionSelectorRowObserver* observer);
 
   void PermissionChanged(const WebsiteSettingsUI::PermissionInfo& permission);
 
- protected:
-  // Overridden from views::View.
-  void ChildPreferredSizeChanged(View* child) override;
-
  private:
-  ~PermissionSelectorRow() override;
+  friend class test::WebsiteSettingsPopupViewTestApi;
 
   void InitializeMenuButtonView(
       views::GridLayout* layout,
@@ -59,15 +64,21 @@
       views::GridLayout* layout,
       const WebsiteSettingsUI::PermissionInfo& permission);
 
+  // Returns the "button" for this row, which is the control used to change the
+  // permission's value. This is either a |MenuButton| or a |Combobox|.
+  views::View* button();
+
   Profile* profile_;
 
   // Model for the permission's menu.
   std::unique_ptr<PermissionMenuModel> menu_model_;
   std::unique_ptr<internal::ComboboxModelAdapter> combobox_model_adapter_;
 
-  views::ImageView* icon_;  // Owned by the views hierachy.
-  internal::PermissionMenuButton* menu_button_;  // Owned by the views hierachy.
-  internal::PermissionCombobox* combobox_;  // Owned by the views hierarchy.
+  // These are all owned by the views hierarchy:
+  views::ImageView* icon_;
+  views::Label* label_;
+  internal::PermissionMenuButton* menu_button_;
+  internal::PermissionCombobox* combobox_;
 
   base::ObserverList<PermissionSelectorRowObserver, false> observer_list_;
 
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
index 98b2ed0..707d035b 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
@@ -97,6 +97,9 @@
 // Spacing between rows in the site settings section
 const int kPermissionsVerticalSpacing = 12;
 
+// Spacing between the label and the menu.
+const int kPermissionMenuSpacing = 16;
+
 // Button/styled label/link IDs ------------------------------------------------
 const int BUTTON_CLOSE = 1337;
 const int STYLED_LABEL_SECURITY_DETAILS = 1338;
@@ -464,7 +467,8 @@
     const WebsiteSettingsUI::PermissionInfo& permission) {
   presenter_->OnSitePermissionChanged(permission.type, permission.setting);
   // The menu buttons for the permissions might have longer strings now, so we
-  // need to size the whole bubble.
+  // need to layout and size the whole bubble.
+  Layout();
   SizeToContents();
 }
 
@@ -627,16 +631,29 @@
                         views::GridLayout::USE_PREF,
                         0,
                         0);
+  const int permissions_column = 1;
+  views::ColumnSet* permissions_set = layout->AddColumnSet(permissions_column);
+  permissions_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+                             0, views::GridLayout::FIXED,
+                             kPermissionIconColumnWidth, 0);
+  permissions_set->AddPaddingColumn(0, kPermissionIconMarginLeft);
+  permissions_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+                             0, views::GridLayout::USE_PREF, 0, 0);
+  permissions_set->AddPaddingColumn(1, kPermissionMenuSpacing);
+  permissions_set->AddColumn(views::GridLayout::TRAILING,
+                             views::GridLayout::FILL, 0,
+                             views::GridLayout::USE_PREF, 0, 0);
   for (const auto& permission : permission_info_list) {
-    layout->StartRow(1, content_column);
-    PermissionSelectorRow* selector = new PermissionSelectorRow(
-        profile_,
-        web_contents() ? web_contents()->GetVisibleURL() : GURL::EmptyGURL(),
-        permission);
+    layout->StartRow(1, permissions_column);
+    std::unique_ptr<PermissionSelectorRow> selector =
+        base::MakeUnique<PermissionSelectorRow>(
+            profile_,
+            web_contents() ? web_contents()->GetVisibleURL()
+                           : GURL::EmptyGURL(),
+            permission, layout);
     selector->AddObserver(this);
-    layout->AddView(selector, 1, 1, views::GridLayout::FILL,
-                    views::GridLayout::CENTER);
     layout->AddPaddingRow(1, kPermissionsVerticalSpacing);
+    selector_rows_.push_back(std::move(selector));
   }
 
   for (auto& object : chosen_object_info_list) {
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view.h b/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
index 0a5e461..f355c694 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/website_settings/chosen_object_row_observer.h"
+#include "chrome/browser/ui/views/website_settings/permission_selector_row.h"
 #include "chrome/browser/ui/views/website_settings/permission_selector_row_observer.h"
 #include "chrome/browser/ui/website_settings/website_settings_ui.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -172,6 +173,11 @@
   // The certificate provided by the site, if one exists.
   scoped_refptr<net::X509Certificate> certificate_;
 
+  // These rows bundle together all the |View|s involved in a single row of the
+  // permissions section, and keep those views updated when the underlying
+  // |Permission| changes.
+  std::vector<std::unique_ptr<PermissionSelectorRow>> selector_rows_;
+
   base::WeakPtrFactory<WebsiteSettingsPopupView> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WebsiteSettingsPopupView);
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view_unittest.cc b/chrome/browser/ui/views/website_settings/website_settings_popup_view_unittest.cc
index 6b6db78..5e9255dc 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view_unittest.cc
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view_unittest.cc
@@ -56,13 +56,21 @@
   views::View* permissions_view() { return view_->permissions_view_; }
 
   PermissionSelectorRow* GetPermissionSelectorAt(int index) {
-    return static_cast<PermissionSelectorRow*>(
-        permissions_view()->child_at(index));
+    return view_->selector_rows_[index].get();
+  }
+
+  // Returns the permission label text of the |index|th permission selector row.
+  // This function returns an empty string if the permission selector row's
+  // |label_| element isn't actually a |views::Label|.
+  base::string16 GetPermissionLabelTextAt(int index) {
+    views::View* view = GetPermissionSelectorAt(index)->label_;
+    if (view->GetClassName() == views::Label::kViewClassName)
+      return static_cast<views::Label*>(view)->text();
+    return base::string16();
   }
 
   base::string16 GetPermissionButtonTextAt(int index) {
-    const int kButtonIndex = 2;  // Button should be the third child.
-    views::View* view = GetPermissionSelectorAt(index)->child_at(kButtonIndex);
+    views::View* view = GetPermissionSelectorAt(index)->button();
     if (view->GetClassName() == views::MenuButton::kViewClassName) {
       return static_cast<views::MenuButton*>(view)->GetText();
     } else if (view->GetClassName() == views::Combobox::kViewClassName) {
@@ -165,6 +173,9 @@
 #define MAYBE_SetPermissionInfo SetPermissionInfo
 #endif
 
+// Each permission selector row is like this: [icon] [label] [selector]
+constexpr int kViewsPerPermissionRow = 3;
+
 // Test UI construction and reconstruction via
 // WebsiteSettingsPopupView::SetPermissionInfo().
 TEST_F(WebsiteSettingsPopupViewTest, MAYBE_SetPermissionInfo) {
@@ -175,7 +186,8 @@
   list.back().setting = CONTENT_SETTING_DEFAULT;
 
   const int kExpectedChildren =
-      ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() ? 11 : 13;
+      kViewsPerPermissionRow *
+      (ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() ? 11 : 13);
   EXPECT_EQ(kExpectedChildren, api_->permissions_view()->child_count());
 
   list.back().setting = CONTENT_SETTING_ALLOW;
@@ -183,15 +195,10 @@
   EXPECT_EQ(kExpectedChildren, api_->permissions_view()->child_count());
 
   PermissionSelectorRow* selector = api_->GetPermissionSelectorAt(0);
-  EXPECT_EQ(3, selector->child_count());
+  EXPECT_TRUE(selector);
 
   // Verify labels match the settings on the PermissionInfoList.
-  const int kLabelIndex = 1;
-  EXPECT_EQ(views::Label::kViewClassName,
-            selector->child_at(kLabelIndex)->GetClassName());
-  views::Label* label =
-      static_cast<views::Label*>(selector->child_at(kLabelIndex));
-  EXPECT_EQ(base::ASCIIToUTF16("Location"), label->text());
+  EXPECT_EQ(base::ASCIIToUTF16("Location"), api_->GetPermissionLabelTextAt(0));
   EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionButtonTextAt(0));
 
   // Verify calling SetPermisisonInfo() directly updates the UI.
@@ -221,7 +228,8 @@
 // Test UI construction and reconstruction with USB devices.
 TEST_F(WebsiteSettingsPopupViewTest, SetPermissionInfoWithUsbDevice) {
   const int kExpectedChildren =
-      ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() ? 11 : 13;
+      kViewsPerPermissionRow *
+      (ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() ? 11 : 13);
   EXPECT_EQ(kExpectedChildren, api_->permissions_view()->child_count());
 
   const GURL origin = GURL(kUrl).GetOrigin();
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index 0f6e454..2317c5b 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -15,10 +15,12 @@
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
+#include "chrome/browser/supervised_user/supervised_user_interstitial.h"
 #include "chrome/common/features.h"
 #include "chrome/common/url_constants.h"
 #include "components/grit/components_resources.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -65,7 +67,7 @@
 // not used in displaying any real interstitials.
 class InterstitialHTMLSource : public content::URLDataSource {
  public:
-  InterstitialHTMLSource() {}
+  explicit InterstitialHTMLSource(Profile* profile) : profile_(profile) {}
   ~InterstitialHTMLSource() override {}
 
   // content::URLDataSource:
@@ -80,6 +82,8 @@
       const content::URLDataSource::GotDataCallback& callback) override;
 
  private:
+  Profile* profile_;
+
   DISALLOW_COPY_AND_ASSIGN(InterstitialHTMLSource);
 };
 
@@ -308,7 +312,7 @@
 InterstitialUI::InterstitialUI(content::WebUI* web_ui)
     : WebUIController(web_ui) {
   Profile* profile = Profile::FromWebUI(web_ui);
-  content::URLDataSource::Add(profile, new InterstitialHTMLSource());
+  content::URLDataSource::Add(profile, new InterstitialHTMLSource(profile));
 }
 
 InterstitialUI::~InterstitialUI() {
@@ -365,7 +369,12 @@
   }
 #endif
   std::string html;
-  if (interstitial_delegate.get()) {
+  if (base::StartsWith(path, "supervised_user", base::CompareCase::SENSITIVE)) {
+    html = SupervisedUserInterstitial::GetHTMLContents(
+        profile_, profile_->IsChild()
+                      ? supervised_user_error_page::ASYNC_CHECKER
+                      : supervised_user_error_page::MANUAL);
+  } else if (interstitial_delegate.get()) {
     html = interstitial_delegate.get()->GetHTMLContents();
   } else {
     html = ResourceBundle::GetSharedInstance()
diff --git a/chrome/browser/ui/webui/physical_web/physical_web_ui.cc b/chrome/browser/ui/webui/physical_web/physical_web_ui.cc
index a37fb45..c7de25f 100644
--- a/chrome/browser/ui/webui/physical_web/physical_web_ui.cc
+++ b/chrome/browser/ui/webui/physical_web/physical_web_ui.cc
@@ -25,6 +25,8 @@
 
   source->AddLocalizedString(physical_web_ui::kTitle,
                              IDS_PHYSICAL_WEB_UI_TITLE);
+  source->AddLocalizedString(physical_web_ui::kEmptyMessage,
+                             IDS_PHYSICAL_WEB_UI_EMPTY_MESSAGE);
   source->SetJsonPath("strings.js");
   source->AddResourcePath(physical_web_ui::kPhysicalWebJS,
                           IDR_PHYSICAL_WEB_UI_JS);
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
index c67289d6..3d78bada 100644
--- a/chrome/browser/ui/webui/snippets_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -75,8 +75,6 @@
       return "ALL_SUGGESTIONS_EXPLICITLY_DISABLED";
     case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED:
       return "CATEGORY_EXPLICITLY_DISABLED";
-    case CategoryStatus::SIGNED_OUT:
-      return "SIGNED_OUT";
     case CategoryStatus::LOADING_ERROR:
       return "LOADING_ERROR";
   }
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index aa45e9b..ea2fd186 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -747,6 +747,7 @@
 const size_t kNumberOfChromeHostURLs = arraysize(kChromeHostURLs);
 
 const char* const kChromeDebugURLs[] = {content::kChromeUIBadCastCrashURL,
+                                        content::kChromeUIBrowserCrashURL,
                                         content::kChromeUICrashURL,
                                         content::kChromeUIDumpURL,
                                         content::kChromeUIKillURL,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
index 11efcc21..a7c486b2 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
@@ -44,7 +44,7 @@
     }
 
     @Override
-    public void onLoadStarted(Tab tab, boolean toDifferentDocument) {
+    public void onPageLoadStarted(Tab tab, String url) {
         mTabLoadStartedCallback.notifyCalled();
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java
index c806196..b2969cad 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java
@@ -91,6 +91,13 @@
     }
 
     /**
+     * Notifies the observer that a full refresh is required.
+     */
+    public void fireFullRefreshRequired() {
+        mObserver.onFullRefreshRequired();
+    }
+
+    /**
      * Removes a category from the fake source without notifying anyone.
      */
     public void silentlyRemoveCategory(int category) {
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index c2980ebe..8896b386 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -24,7 +24,7 @@
 #include "components/network_time/network_time_tracker.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/prefs/pref_service.h"
-#include "components/subresource_filter/core/browser/ruleset_service.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "content/public/browser/notification_service.h"
 #include "extensions/features/features.h"
 #include "media/media_features.h"
@@ -222,7 +222,7 @@
   return nullptr;
 }
 
-subresource_filter::RulesetService*
+subresource_filter::ContentRulesetService*
 TestingBrowserProcess::subresource_filter_ruleset_service() {
   return subresource_filter_ruleset_service_.get();
 }
@@ -461,8 +461,9 @@
 }
 
 void TestingBrowserProcess::SetRulesetService(
-    std::unique_ptr<subresource_filter::RulesetService> ruleset_service) {
-  subresource_filter_ruleset_service_.swap(ruleset_service);
+    std::unique_ptr<subresource_filter::ContentRulesetService>
+        content_ruleset_service) {
+  subresource_filter_ruleset_service_.swap(content_ruleset_service);
 }
 
 void TestingBrowserProcess::SetRapporServiceImpl(
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 041c263..421de7e 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -85,8 +85,8 @@
   safe_browsing::SafeBrowsingService* safe_browsing_service() override;
   safe_browsing::ClientSideDetectionService* safe_browsing_detection_service()
       override;
-  subresource_filter::RulesetService* subresource_filter_ruleset_service()
-      override;
+  subresource_filter::ContentRulesetService*
+  subresource_filter_ruleset_service() override;
   net::URLRequestContextGetter* system_request_context() override;
   BrowserProcessPlatformPart* platform_part() override;
 
@@ -141,7 +141,8 @@
   void SetIOThread(IOThread* io_thread);
   void SetSafeBrowsingService(safe_browsing::SafeBrowsingService* sb_service);
   void SetRulesetService(
-      std::unique_ptr<subresource_filter::RulesetService> ruleset_service);
+      std::unique_ptr<subresource_filter::ContentRulesetService>
+          ruleset_service);
   void SetSystemRequestContext(net::URLRequestContextGetter* context_getter);
   void SetNotificationUIManager(
       std::unique_ptr<NotificationUIManager> notification_ui_manager);
@@ -179,7 +180,7 @@
 #endif
 
   scoped_refptr<safe_browsing::SafeBrowsingService> sb_service_;
-  std::unique_ptr<subresource_filter::RulesetService>
+  std::unique_ptr<subresource_filter::ContentRulesetService>
       subresource_filter_ruleset_service_;
 
   std::unique_ptr<network_time::NetworkTimeTracker> network_time_tracker_;
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 2ac40793..5ffb754 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -107,7 +107,8 @@
     _REVISION_NEGATIVE_FILTER['HEAD'] + [
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1674
         'WindowSwitchingTest.testShouldBeAbleToIterateOverAllOpenWindows',
-]
+    ]
+)
 _REVISION_NEGATIVE_FILTER['57'] = (
     _REVISION_NEGATIVE_FILTER['HEAD'] + [
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 6ae15530..bf8e156 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -439,15 +439,14 @@
   memory_pressure_monitor_.reset(new CastMemoryPressureMonitor());
 #endif  // defined(OS_ANDROID)
 
+  cast_browser_process_->SetNetLog(net_log_.get());
+  url_request_context_factory_->InitializeOnUIThread(net_log_.get());
+
   cast_browser_process_->SetConnectivityChecker(ConnectivityChecker::Create(
       content::BrowserThread::GetTaskRunnerForThread(
           content::BrowserThread::IO),
       url_request_context_factory_->GetSystemGetter()));
 
-  cast_browser_process_->SetNetLog(net_log_.get());
-
-  url_request_context_factory_->InitializeOnUIThread(net_log_.get());
-
   cast_browser_process_->SetBrowserContext(
       base::MakeUnique<CastBrowserContext>(url_request_context_factory_));
   cast_browser_process_->SetMetricsServiceClient(
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index b6def5f..658c5ee9 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -253,6 +253,7 @@
   // in the future.
   http_server_properties_.reset(new net::HttpServerPropertiesImpl);
 
+  DCHECK(proxy_config_service_);
   proxy_service_ = net::ProxyService::CreateUsingSystemProxyResolver(
       std::move(proxy_config_service_), 0, NULL);
   system_dependencies_initialized_ = true;
diff --git a/cloud_print/DEPS b/cloud_print/DEPS
index edd33b74..51643dee 100644
--- a/cloud_print/DEPS
+++ b/cloud_print/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
   "+chrome/common",
   "+chrome/installer/launcher_support",
-  "+virtual_driver_setup_resources",
 ]
diff --git a/cloud_print/virtual_driver/win/install/BUILD.gn b/cloud_print/virtual_driver/win/install/BUILD.gn
index 2c8fbaa0..17b7fd2 100644
--- a/cloud_print/virtual_driver/win/install/BUILD.gn
+++ b/cloud_print/virtual_driver/win/install/BUILD.gn
@@ -62,7 +62,7 @@
 
   # TODO(thakis): Consider removing this in favor of the default directory,
   # https://crbug.com/697092
-  output_dir = "$root_gen_dir/virtual_driver_setup_resources"
+  # output_dir = "$root_gen_dir/virtual_driver_setup_resources"
 
   outputs = [
     "grit/virtual_driver_setup_resources.h",
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc
index 64eef52..70ae0dd 100644
--- a/cloud_print/virtual_driver/win/install/setup.cc
+++ b/cloud_print/virtual_driver/win/install/setup.cc
@@ -21,9 +21,9 @@
 #include "base/win/windows_version.h"
 #include "cloud_print/common/win/cloud_print_utils.h"
 #include "cloud_print/common/win/install_utils.h"
+#include "cloud_print/virtual_driver/win/install/grit/virtual_driver_setup_resources.h"
 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
-#include "virtual_driver_setup_resources/grit/virtual_driver_setup_resources.h"
 
 #include <strsafe.h>  // Must be after base headers to avoid deprecation
                       // warnings.
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index d31e213f..aed44577 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -37,6 +37,8 @@
 
 namespace {
 
+using Logger = autofill::SavePasswordProgressLogger;
+
 // Returns true if we think that this form is for account creation. |passwords|
 // is filled with the password field(s) in the form.
 bool GetAccountCreationPasswordFields(
@@ -142,7 +144,7 @@
       form_classifier_enabled_(false),
       password_agent_(password_agent),
       binding_(this) {
-  VLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
+  LogBoolean(Logger::STRING_GENERATION_RENDERER_ENABLED, enabled_);
   // PasswordGenerationAgent is guaranteed to outlive |render_frame|.
   render_frame->GetInterfaceRegistry()->AddInterface(base::Bind(
       &PasswordGenerationAgent::BindRequest, base::Unretained(this)));
@@ -258,7 +260,7 @@
     std::unique_ptr<PasswordForm> password_form(
         CreatePasswordFormFromWebForm(forms[i], nullptr, nullptr));
     if (!password_form.get()) {
-      VLOG(2) << "Skipping form as it would not be saved";
+      LogMessage(Logger::STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM);
       continue;
     }
 
@@ -281,13 +283,14 @@
   }
 
   if (!possible_account_creation_forms_.empty()) {
-    VLOG(2) << possible_account_creation_forms_.size()
-            << " possible account creation forms deteceted";
+    LogNumber(
+        Logger::STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS,
+        possible_account_creation_forms_.size());
     DetermineGenerationElement();
   }
 }
 
-bool PasswordGenerationAgent::ShouldAnalyzeDocument() const {
+bool PasswordGenerationAgent::ShouldAnalyzeDocument() {
   // Make sure that this security origin is allowed to use password manager.
   // Generating a password that can't be saved is a bad idea.
   if (!render_frame() ||
@@ -296,7 +299,7 @@
            ->document()
            .getSecurityOrigin()
            .canAccessPasswordManager()) {
-    VLOG(1) << "No PasswordManager access";
+    LogMessage(Logger::STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS);
     return false;
   }
 
@@ -313,6 +316,7 @@
   password_is_generated_ = true;
   password_generation::LogPasswordGenerationEvent(
       password_generation::PASSWORD_ACCEPTED);
+  LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED);
   for (auto& password_element : generation_form_data_->password_elements) {
     password_element.setValue(blink::WebString::fromUTF16(password),
                               true /* sendEvents */);
@@ -368,15 +372,14 @@
 
 void PasswordGenerationAgent::DetermineGenerationElement() {
   if (generation_form_data_) {
-    VLOG(2) << "Account creation form already found";
+    LogMessage(Logger::STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND);
     return;
   }
 
   // Make sure local heuristics have identified a possible account creation
   // form.
   if (possible_account_creation_forms_.empty()) {
-    VLOG(2) << "Local hueristics have not detected a possible account "
-             << "creation form";
+    LogMessage(Logger::STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS);
     return;
   }
 
@@ -390,28 +393,23 @@
       VLOG(2) << "Bypassing additional checks.";
     } else if (!ContainsURL(not_blacklisted_password_form_origins_,
                             possible_password_form->origin)) {
-      VLOG(2) << "Have not received confirmation that password form isn't "
-               << "blacklisted";
+      LogMessage(Logger::STRING_GENERATION_RENDERER_NOT_BLACKLISTED);
       continue;
     } else {
       generation_data = FindFormGenerationData(generation_enabled_forms_,
                                                *possible_password_form);
       if (!generation_data) {
         if (AutocompleteAttributesSetForGeneration(*possible_password_form)) {
-          VLOG(2) << "Ignoring lack of Autofill signal due to Autocomplete "
-                  << "attributes";
+          LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE);
           password_generation::LogPasswordGenerationEvent(
               password_generation::AUTOCOMPLETE_ATTRIBUTES_ENABLED_GENERATION);
         } else {
-          VLOG(2)
-              << "Have not received confirmation from Autofill that form is "
-              << "used for account creation";
+          LogMessage(Logger::STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL);
           continue;
         }
       }
     }
-
-    VLOG(2) << "Password generation eligible form found";
+    LogMessage(Logger::STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND);
     std::vector<blink::WebInputElement> password_elements =
         generation_data
             ? FindPasswordElementsForGeneration(
@@ -419,7 +417,7 @@
             : possible_form_data.password_elements;
     if (password_elements.empty()) {
       // It might be if JavaScript changes field names.
-      VLOG(2) << "Fields for generation are not found";
+      LogMessage(Logger::STRING_GENERATION_RENDERER_NO_FIELD_FOUND);
       return;
     }
     generation_form_data_.reset(new AccountCreationFormData(
@@ -522,6 +520,7 @@
 void PasswordGenerationAgent::ShowGenerationPopup() {
   if (!render_frame())
     return;
+  LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP);
   GetPasswordManagerClient()->ShowPasswordGenerationPopup(
       render_frame()->GetRenderView()->ElementBoundsInWindow(
           generation_element_),
@@ -601,4 +600,27 @@
   return password_manager_client_;
 }
 
+void PasswordGenerationAgent::LogMessage(Logger::StringID message_id) {
+  if (!password_agent_->logging_state_active())
+    return;
+  RendererSavePasswordProgressLogger logger(GetPasswordManagerDriver().get());
+  logger.LogMessage(message_id);
+}
+
+void PasswordGenerationAgent::LogBoolean(Logger::StringID message_id,
+                                         bool truth_value) {
+  if (!password_agent_->logging_state_active())
+    return;
+  RendererSavePasswordProgressLogger logger(GetPasswordManagerDriver().get());
+  logger.LogBoolean(message_id, truth_value);
+}
+
+void PasswordGenerationAgent::LogNumber(Logger::StringID message_id,
+                                        int number) {
+  if (!password_agent_->logging_state_active())
+    return;
+  RendererSavePasswordProgressLogger logger(GetPasswordManagerDriver().get());
+  logger.LogNumber(message_id, number);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index 694c5fc..c7cb5fb2 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -16,6 +16,7 @@
 #include "base/memory/linked_ptr.h"
 #include "components/autofill/content/common/autofill_agent.mojom.h"
 #include "components/autofill/content/common/autofill_driver.mojom.h"
+#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/WebKit/public/web/WebInputElement.h"
@@ -66,7 +67,7 @@
  protected:
   // Returns true if the document for |render_frame()| is one that we should
   // consider analyzing. Virtual so that it can be overriden during testing.
-  virtual bool ShouldAnalyzeDocument() const;
+  virtual bool ShouldAnalyzeDocument();
 
   // Use to force enable during testing.
   void set_enabled(bool enabled) { enabled_ = enabled; }
@@ -118,6 +119,12 @@
   void RunFormClassifierAndSaveVote(const blink::WebFormElement& web_form,
                                     const PasswordForm& form);
 
+  void LogMessage(autofill::SavePasswordProgressLogger::StringID message_id);
+  void LogBoolean(autofill::SavePasswordProgressLogger::StringID message_id,
+                  bool truth_value);
+  void LogNumber(autofill::SavePasswordProgressLogger::StringID message_id,
+                 int number);
+
   // Creates a password form to presave a generated password. It copies behavior
   // of CreatePasswordFormFromWebForm/FromUnownedInputElements, but takes
   // |password_value| from |generation_element_| and empties |username_value|.
diff --git a/components/autofill/content/renderer/test_password_generation_agent.cc b/components/autofill/content/renderer/test_password_generation_agent.cc
index d8707b0f..84d937a79 100644
--- a/components/autofill/content/renderer/test_password_generation_agent.cc
+++ b/components/autofill/content/renderer/test_password_generation_agent.cc
@@ -15,7 +15,7 @@
 
 TestPasswordGenerationAgent::~TestPasswordGenerationAgent() {}
 
-bool TestPasswordGenerationAgent::ShouldAnalyzeDocument() const {
+bool TestPasswordGenerationAgent::ShouldAnalyzeDocument() {
   return true;
 }
 
diff --git a/components/autofill/content/renderer/test_password_generation_agent.h b/components/autofill/content/renderer/test_password_generation_agent.h
index 333b4321..11aef9d 100644
--- a/components/autofill/content/renderer/test_password_generation_agent.h
+++ b/components/autofill/content/renderer/test_password_generation_agent.h
@@ -21,7 +21,7 @@
 
   // PasswordGenreationAgent implementation:
   // Always return true to allow loading of data URLs.
-  bool ShouldAnalyzeDocument() const override;
+  bool ShouldAnalyzeDocument() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestPasswordGenerationAgent);
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 73be9b49..8a55fdd 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -390,6 +390,39 @@
       return "Form votes";
     case SavePasswordProgressLogger::STRING_REUSE_FOUND:
       return "Password reused from ";
+    case SavePasswordProgressLogger::STRING_GENERATION_DISABLED_SAVING_DISABLED:
+      return "Generation disabled: saving disabled";
+    case SavePasswordProgressLogger::STRING_GENERATION_DISABLED_NO_SYNC:
+      return "Generation disabled: no sync";
+    case SavePasswordProgressLogger::
+        STRING_GENERATION_DISABLED_CUSTOM_PASSPHRASE:
+      return "Generation disabled: custom passphrase";
+    case STRING_GENERATION_RENDERER_ENABLED:
+      return "Generation renderer enabled";
+    case STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM:
+      return "Generation invalid PasswordForm";
+    case STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS:
+      return "Generation possible account creation forms";
+    case STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS:
+      return "Generation: no PasswordManager access";
+    case STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND:
+      return "Generation: account creation form already found";
+    case STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS:
+      return "Generation: no possible account creation forms";
+    case STRING_GENERATION_RENDERER_NOT_BLACKLISTED:
+      return "Generation: no non-blacklisted confirmation";
+    case STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE:
+      return "Generation: autocomplete attributes found";
+    case STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL:
+      return "Generation: no server signal";
+    case STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND:
+      return "Generation: eligible form found";
+    case STRING_GENERATION_RENDERER_NO_FIELD_FOUND:
+      return "Generation: fields for generation are not found";
+    case STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP:
+      return "Show generation popup";
+    case STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED:
+      return "Generated password accepted";
     case SavePasswordProgressLogger::STRING_INVALID:
       return "INVALID";
       // Intentionally no default: clause here -- all IDs need to get covered.
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index de8a082..19c9e04c 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -150,6 +150,22 @@
     STRING_SERVER_PREDICTIONS,
     STRING_FORM_VOTES,
     STRING_REUSE_FOUND,
+    STRING_GENERATION_DISABLED_SAVING_DISABLED,
+    STRING_GENERATION_DISABLED_NO_SYNC,
+    STRING_GENERATION_DISABLED_CUSTOM_PASSPHRASE,
+    STRING_GENERATION_RENDERER_ENABLED,
+    STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM,
+    STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS,
+    STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS,
+    STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND,
+    STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS,
+    STRING_GENERATION_RENDERER_NOT_BLACKLISTED,
+    STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE,
+    STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL,
+    STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND,
+    STRING_GENERATION_RENDERER_NO_FIELD_FOUND,
+    STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP,
+    STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED,
     STRING_INVALID,  // Represents a string returned in a case of an error.
     STRING_MAX = STRING_INVALID
   };
diff --git a/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc b/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
index 21ecd811..c2688710 100644
--- a/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
+++ b/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/bookmarks/browser/bookmark_model.h"
@@ -29,9 +30,8 @@
   base::Time last_visited;
 };
 
-const char* kBookmarksURLBlacklist[] = {"chrome://newtab/",
-                                        "chrome-native://newtab/",
-                                        "chrome://bookmarks/"};
+const char* kBookmarksURLBlacklist[] = {
+    "chrome://newtab/", "chrome-native://newtab/", "chrome://bookmarks/"};
 
 const char kBookmarkLastVisitDateOnMobileKey[] = "last_visited";
 const char kBookmarkLastVisitDateOnDesktopKey[] = "last_visited_desktop";
@@ -106,10 +106,11 @@
   // If there are bookmarks for |url|, set their last visit date to now.
   std::string now = FormatLastVisitDate(base::Time::Now());
   for (const BookmarkNode* node : bookmarks_for_url) {
-    bookmark_model->SetNodeMetaInfo(
-        node, is_mobile_platform ? kBookmarkLastVisitDateOnMobileKey
-                                 : kBookmarkLastVisitDateOnDesktopKey,
-        now);
+    bookmark_model->SetNodeMetaInfo(node,
+                                    is_mobile_platform
+                                        ? kBookmarkLastVisitDateOnMobileKey
+                                        : kBookmarkLastVisitDateOnDesktopKey,
+                                    now);
     // If the bookmark has been dismissed from NTP before, a new visit overrides
     // such a dismissal.
     bookmark_model->DeleteNodeMetaInfo(node, kBookmarkDismissedFromNTP);
@@ -243,22 +244,19 @@
   bookmark_model->GetBookmarks(&bookmarks);
 
   // Remove the bookmark URLs which have at least one non-dismissed bookmark.
-  bookmarks.erase(
-      std::remove_if(
-          bookmarks.begin(), bookmarks.end(),
-          [&bookmark_model](const BookmarkModel::URLAndTitle& bookmark) {
-            std::vector<const BookmarkNode*> bookmarks_for_url;
-            bookmark_model->GetNodesByURL(bookmark.url, &bookmarks_for_url);
-            DCHECK(!bookmarks_for_url.empty());
+  base::EraseIf(
+      bookmarks, [&bookmark_model](const BookmarkModel::URLAndTitle& bookmark) {
+        std::vector<const BookmarkNode*> bookmarks_for_url;
+        bookmark_model->GetNodesByURL(bookmark.url, &bookmarks_for_url);
+        DCHECK(!bookmarks_for_url.empty());
 
-            for (const BookmarkNode* node : bookmarks_for_url) {
-              if (!IsDismissedFromNTPForBookmark(*node)) {
-                return true;
-              }
-            }
-            return false;
-          }),
-      bookmarks.end());
+        for (const BookmarkNode* node : bookmarks_for_url) {
+          if (!IsDismissedFromNTPForBookmark(*node)) {
+            return true;
+          }
+        }
+        return false;
+      });
 
   // Insert into |result|.
   std::vector<const BookmarkNode*> result;
@@ -306,7 +304,7 @@
       ClearLastVisitedMetadataIfBetween(bookmark_model, *bookmark, begin, end,
                                         kBookmarkLastVisitDateOnMobileKey);
       ClearLastVisitedMetadataIfBetween(bookmark_model, *bookmark, begin, end,
-                                kBookmarkLastVisitDateOnDesktopKey);
+                                        kBookmarkLastVisitDateOnDesktopKey);
     }
   }
 }
diff --git a/components/ntp_snippets/category_status.h b/components/ntp_snippets/category_status.h
index 2f6c7cd..caf0ddf 100644
--- a/components/ntp_snippets/category_status.h
+++ b/components/ntp_snippets/category_status.h
@@ -31,9 +31,6 @@
   // the service configuration.
   CATEGORY_EXPLICITLY_DISABLED,
 
-  // Content suggestions are not available because the user is not signed in.
-  SIGNED_OUT,
-
   // Content suggestions are not available because an error occurred when
   // loading or updating them.
   LOADING_ERROR
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index 3e517b7e..81acdbe0 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -74,13 +74,11 @@
 void EraseByPrimaryID(RemoteSuggestion::PtrVector* suggestions,
                       const std::vector<std::string>& ids) {
   std::set<std::string> ids_lookup(ids.begin(), ids.end());
-  suggestions->erase(
-      std::remove_if(
-          suggestions->begin(), suggestions->end(),
-          [&ids_lookup](const std::unique_ptr<RemoteSuggestion>& suggestion) {
-            return base::ContainsValue(ids_lookup, suggestion->id());
-          }),
-      suggestions->end());
+  base::EraseIf(
+      *suggestions,
+      [&ids_lookup](const std::unique_ptr<RemoteSuggestion>& suggestion) {
+        return base::ContainsValue(ids_lookup, suggestion->id());
+      });
 }
 
 void EraseMatchingSuggestions(
@@ -91,23 +89,18 @@
     const std::vector<std::string>& suggestion_ids = suggestion->GetAllIDs();
     compare_against_ids.insert(suggestion_ids.begin(), suggestion_ids.end());
   }
-  suggestions->erase(
-      std::remove_if(suggestions->begin(), suggestions->end(),
-                     [&compare_against_ids](
-                         const std::unique_ptr<RemoteSuggestion>& suggestion) {
-                       return HasIntersection(suggestion->GetAllIDs(),
-                                              compare_against_ids);
-                     }),
-      suggestions->end());
+  base::EraseIf(
+      *suggestions, [&compare_against_ids](
+                        const std::unique_ptr<RemoteSuggestion>& suggestion) {
+        return HasIntersection(suggestion->GetAllIDs(), compare_against_ids);
+      });
 }
 
 void RemoveNullPointers(RemoteSuggestion::PtrVector* suggestions) {
-  suggestions->erase(
-      std::remove_if(suggestions->begin(), suggestions->end(),
-                     [](const std::unique_ptr<RemoteSuggestion>& suggestion) {
-                       return !suggestion;
-                     }),
-      suggestions->end());
+  base::EraseIf(*suggestions,
+                [](const std::unique_ptr<RemoteSuggestion>& suggestion) {
+                  return !suggestion;
+                });
 }
 
 void RemoveIncompleteSuggestions(RemoteSuggestion::PtrVector* suggestions) {
@@ -118,12 +111,10 @@
   int num_suggestions = suggestions->size();
   // Remove suggestions that do not have all the info we need to display it to
   // the user.
-  suggestions->erase(
-      std::remove_if(suggestions->begin(), suggestions->end(),
-                     [](const std::unique_ptr<RemoteSuggestion>& suggestion) {
-                       return !suggestion->is_complete();
-                     }),
-      suggestions->end());
+  base::EraseIf(*suggestions,
+                [](const std::unique_ptr<RemoteSuggestion>& suggestion) {
+                  return !suggestion->is_complete();
+                });
   int num_suggestions_removed = num_suggestions - suggestions->size();
   UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
                         num_suggestions_removed > 0);
@@ -244,9 +235,8 @@
     const ContentSuggestion::ID& suggestion_id,
     const GURL& url,
     const ImageFetchedCallback& callback) {
-  if (url.is_empty() ||
-      !thumbnail_requests_throttler_.DemandQuotaForRequest(
-          /*interactive_request=*/true)) {
+  if (url.is_empty() || !thumbnail_requests_throttler_.DemandQuotaForRequest(
+                            /*interactive_request=*/true)) {
     // Return an empty image. Directly, this is never synchronous with the
     // original FetchSuggestionImage() call - an asynchronous database query has
     // happened in the meantime.
@@ -278,9 +268,7 @@
       category_ranker_(category_ranker),
       suggestions_fetcher_(std::move(suggestions_fetcher)),
       database_(std::move(database)),
-      image_fetcher_(std::move(image_fetcher),
-                     pref_service,
-                     database_.get()),
+      image_fetcher_(std::move(image_fetcher), pref_service, database_.get()),
       status_service_(std::move(status_service)),
       fetch_when_ready_(false),
       fetch_when_ready_interactive_(false),
diff --git a/components/password_manager/core/browser/password_generation_manager.cc b/components/password_manager/core/browser/password_generation_manager.cc
index 2e6f35f..a30adf8 100644
--- a/components/password_manager/core/browser/password_generation_manager.cc
+++ b/components/password_manager/core/browser/password_generation_manager.cc
@@ -9,9 +9,11 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/password_form_generation_data.h"
+#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
 
 using autofill::AutofillField;
 using autofill::FieldSignature;
@@ -20,6 +22,10 @@
 
 namespace password_manager {
 
+namespace {
+using Logger = autofill::SavePasswordProgressLogger;
+}
+
 PasswordGenerationManager::PasswordGenerationManager(
     PasswordManagerClient* client,
     PasswordManagerDriver* driver)
@@ -36,17 +42,15 @@
 
   std::vector<autofill::PasswordFormGenerationData>
       forms_eligible_for_generation;
-  for (auto form_it = forms.begin(); form_it != forms.end(); ++form_it) {
-    FormStructure* form = *form_it;
-    AutofillField* generation_field = nullptr;
-    AutofillField* confirmation_field = nullptr;
-    for (auto field_it = form->begin(); field_it != form->end(); ++field_it) {
-      AutofillField* field = field_it->get();
+  for (const FormStructure* form : forms) {
+    const AutofillField* generation_field = nullptr;
+    const AutofillField* confirmation_field = nullptr;
+    for (const std::unique_ptr<AutofillField>& field : *form) {
       if (field->server_type() == autofill::ACCOUNT_CREATION_PASSWORD ||
           field->server_type() == autofill::NEW_PASSWORD) {
-        generation_field = field;
+        generation_field = field.get();
       } else if (field->server_type() == autofill::CONFIRMATION_PASSWORD) {
-        confirmation_field = field;
+        confirmation_field = field.get();
       }
     }
     if (generation_field) {
@@ -67,16 +71,29 @@
 // (1) Password sync is enabled, and
 // (2) Password saving is enabled.
 bool PasswordGenerationManager::IsGenerationEnabled() const {
+  std::unique_ptr<Logger> logger;
+  if (password_manager_util::IsLoggingActive(client_)) {
+    logger.reset(
+        new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+  }
+
   if (!client_->IsSavingAndFillingEnabledForCurrentPage()) {
-    VLOG(2) << "Generation disabled because password saving is disabled";
+    if (logger)
+      logger->LogMessage(Logger::STRING_GENERATION_DISABLED_SAVING_DISABLED);
     return false;
   }
 
   // Don't consider sync enabled if the user has a custom passphrase. See
   // http://crbug.com/358998 for more details.
   if (client_->GetPasswordSyncState() != SYNCING_NORMAL_ENCRYPTION) {
-    VLOG(2) << "Generation disabled because passwords are not being synced or"
-             << " custom passphrase is used.";
+    if (logger) {
+      if (client_->GetPasswordSyncState() == NOT_SYNCING_PASSWORDS)
+        logger->LogMessage(Logger::STRING_GENERATION_DISABLED_NO_SYNC);
+      else if (client_->GetPasswordSyncState() ==
+               SYNCING_WITH_CUSTOM_PASSPHRASE)
+        logger->LogMessage(
+            Logger::STRING_GENERATION_DISABLED_CUSTOM_PASSPHRASE);
+    }
     return false;
   }
 
diff --git a/components/physical_web/webui/physical_web_ui_constants.cc b/components/physical_web/webui/physical_web_ui_constants.cc
index 82d1af39..ba84fcc 100644
--- a/components/physical_web/webui/physical_web_ui_constants.cc
+++ b/components/physical_web/webui/physical_web_ui_constants.cc
@@ -16,6 +16,7 @@
 
 // Strings.
 const char kTitle[] = "title";
+const char kEmptyMessage[] = "emptyMessage";
 const char kPageInfoDescription[] = "pageInfoDescription";
 const char kPageInfoIcon[] = "pageInfoIcon";
 const char kPageInfoTitle[] = "pageInfoTitle";
diff --git a/components/physical_web/webui/physical_web_ui_constants.h b/components/physical_web/webui/physical_web_ui_constants.h
index 2eb8523..320bf4c2 100644
--- a/components/physical_web/webui/physical_web_ui_constants.h
+++ b/components/physical_web/webui/physical_web_ui_constants.h
@@ -20,6 +20,7 @@
 // Strings.
 // Must match the constants used in the resource files.
 extern const char kTitle[];
+extern const char kEmptyMessage[];
 extern const char kPageInfoDescription[];
 extern const char kPageInfoIcon[];
 extern const char kPageInfoTitle[];
diff --git a/components/physical_web/webui/resources/physical_web.html b/components/physical_web/webui/resources/physical_web.html
index a5a0ba6..e051510 100644
--- a/components/physical_web/webui/resources/physical_web.html
+++ b/components/physical_web/webui/resources/physical_web.html
@@ -20,7 +20,7 @@
 <script src="chrome://physical-web/strings.js"></script>
 </head>
 <body>
-<div id="body-container" style="visibility:visible">
+<div id="body-container" hidden>
 
   <h1 i18n-content="title"></h1>
 
@@ -38,6 +38,9 @@
 
 </div>
 
+<div id="empty-list-container" i18n-content="emptyMessage" hidden>
+</div>
+
 <script src="chrome://resources/js/i18n_template.js"></script>
 <script src="chrome://resources/js/jstemplate_compiled.js"></script>
 </body>
diff --git a/components/physical_web/webui/resources/physical_web.js b/components/physical_web/webui/resources/physical_web.js
index 418dc1d..70d856f 100644
--- a/components/physical_web/webui/resources/physical_web.js
+++ b/components/physical_web/webui/resources/physical_web.js
@@ -24,8 +24,12 @@
 function returnNearbyURLs(nearbyUrlsData) {
   var bodyContainer = $('body-container');
   renderTemplate(nearbyUrlsData);
+  bodyContainer.hidden = false;
 
-  bodyContainer.style.visibility = 'visible';
+  if (nearbyUrlsData['metadata'].length == 0) {
+    var emptyMessage = $('empty-list-container');
+    emptyMessage.hidden = false;
+  }
 }
 
 document.addEventListener('DOMContentLoaded', requestNearbyURLs);
diff --git a/components/safe_browsing_db/safe_browsing_prefs.cc b/components/safe_browsing_db/safe_browsing_prefs.cc
index 66c1dbb6..3895131c 100644
--- a/components/safe_browsing_db/safe_browsing_prefs.cc
+++ b/components/safe_browsing_db/safe_browsing_prefs.cc
@@ -57,6 +57,25 @@
   MAX_SBER_PREF
 };
 
+// A histogram for tracking a nullable boolean, which can be false, true or
+// null. These values are written to logs. New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum NullableBoolean {
+  NULLABLE_BOOLEAN_FALSE = 0,
+  NULLABLE_BOOLEAN_TRUE = 1,
+  NULLABLE_BOOLEAN_NULL = 2,
+  MAX_NULLABLE_BOOLEAN
+};
+
+NullableBoolean GetPrefValueOrNull(const PrefService& prefs,
+                                   const std::string& pref_name) {
+  if (!prefs.HasPrefPath(pref_name)) {
+    return NULLABLE_BOOLEAN_NULL;
+  }
+  return prefs.GetBoolean(pref_name) ? NULLABLE_BOOLEAN_TRUE
+                                     : NULLABLE_BOOLEAN_FALSE;
+}
+
 // Update the correct UMA metric based on which pref was changed and which UI
 // the change was made on.
 void RecordExtendedReportingPrefChanged(
@@ -125,6 +144,10 @@
     "safebrowsing.scout_reporting_enabled";
 const char kSafeBrowsingScoutGroupSelected[] =
     "safebrowsing.scout_group_selected";
+const char kSafeBrowsingSawInterstitialExtendedReporting[] =
+    "safebrowsing.saw_interstitial_sber1";
+const char kSafeBrowsingSawInterstitialScoutReporting[] =
+    "safebrowsing.saw_interstitial_sber2";
 }  // namespace prefs
 
 namespace safe_browsing {
@@ -224,12 +247,14 @@
     // group (above) that was reverted. We want to restore the user to a
     // reasonable state based on the ScoutGroup and ScoutReporting preferences.
     UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName, CONTROL, MAX_REASONS);
+    bool transitioned = false;
     if (prefs->GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled)) {
       // User opted-in to Scout which is broader than legacy Extended Reporting.
       // Opt them in to Extended Reporting.
       prefs->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, true);
       UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
                                 ROLLBACK_SBER2_IMPLIES_SBER1, MAX_REASONS);
+      transitioned = true;
     } else if (prefs->GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
       // User was in the Scout Group (ie: seeing the Scout opt-in text) but did
       // NOT opt-in to Scout. Assume this was a conscious choice and remove
@@ -251,12 +276,20 @@
         UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
                                   ROLLBACK_NO_SBER2_CLEAR_SBER1, MAX_REASONS);
       }
+      transitioned = true;
     }
 
     // Also clear both the Scout settings to start over from a clean state and
     // avoid the above logic from triggering on next restart.
     prefs->ClearPref(prefs::kSafeBrowsingScoutGroupSelected);
     prefs->ClearPref(prefs::kSafeBrowsingScoutReportingEnabled);
+
+    // Also forget that the user has seen any interstitials if they're
+    // reverting back to a clean state.
+    if (transitioned) {
+      prefs->ClearPref(prefs::kSafeBrowsingSawInterstitialExtendedReporting);
+      prefs->ClearPref(prefs::kSafeBrowsingSawInterstitialScoutReporting);
+    }
   }
 }
 
@@ -276,26 +309,38 @@
   UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.Extended",
                         IsExtendedReportingEnabled(prefs));
 
+  // Track whether this user has ever seen a security interstitial.
+  UMA_HISTOGRAM_BOOLEAN(
+      "SafeBrowsing.Pref.SawInterstitial.SBER1Pref",
+      prefs.GetBoolean(prefs::kSafeBrowsingSawInterstitialExtendedReporting));
+  UMA_HISTOGRAM_BOOLEAN(
+      "SafeBrowsing.Pref.SawInterstitial.SBER2Pref",
+      prefs.GetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting));
+
   // These metrics track the Scout transition.
   if (prefs.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
     // Users in the Scout group: currently seeing the Scout opt-in.
-    UMA_HISTOGRAM_BOOLEAN(
+    UMA_HISTOGRAM_ENUMERATION(
         "SafeBrowsing.Pref.Scout.ScoutGroup.SBER1Pref",
-        prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled));
-    UMA_HISTOGRAM_BOOLEAN(
+        GetPrefValueOrNull(prefs, prefs::kSafeBrowsingExtendedReportingEnabled),
+        MAX_NULLABLE_BOOLEAN);
+    UMA_HISTOGRAM_ENUMERATION(
         "SafeBrowsing.Pref.Scout.ScoutGroup.SBER2Pref",
-        prefs.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled));
+        GetPrefValueOrNull(prefs, prefs::kSafeBrowsingScoutReportingEnabled),
+        MAX_NULLABLE_BOOLEAN);
   } else {
     // Users not in the Scout group: currently seeing the SBER opt-in.
-    UMA_HISTOGRAM_BOOLEAN(
+    UMA_HISTOGRAM_ENUMERATION(
         "SafeBrowsing.Pref.Scout.NoScoutGroup.SBER1Pref",
-        prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled));
+        GetPrefValueOrNull(prefs, prefs::kSafeBrowsingExtendedReportingEnabled),
+        MAX_NULLABLE_BOOLEAN);
     // The following metric is a corner case. User was previously in the
     // Scout group and was able to opt-in to the Scout pref, but was since
     // removed from the Scout group (eg: by rolling back a Scout experiment).
-    UMA_HISTOGRAM_BOOLEAN(
+    UMA_HISTOGRAM_ENUMERATION(
         "SafeBrowsing.Pref.Scout.NoScoutGroup.SBER2Pref",
-        prefs.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled));
+        GetPrefValueOrNull(prefs, prefs::kSafeBrowsingScoutReportingEnabled),
+        MAX_NULLABLE_BOOLEAN);
   }
 }
 
@@ -384,6 +429,12 @@
                               CAN_SHOW_SCOUT_OPT_IN_SAW_FIRST_INTERSTITIAL,
                               MAX_REASONS);
   }
+
+  // Remember that this user saw an interstitial with the current opt-in text.
+  prefs->SetBoolean(IsScout(*prefs)
+                        ? prefs::kSafeBrowsingSawInterstitialScoutReporting
+                        : prefs::kSafeBrowsingSawInterstitialExtendedReporting,
+                    true);
 }
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing_db/safe_browsing_prefs.h b/components/safe_browsing_db/safe_browsing_prefs.h
index da25461..54666f3 100644
--- a/components/safe_browsing_db/safe_browsing_prefs.h
+++ b/components/safe_browsing_db/safe_browsing_prefs.h
@@ -23,6 +23,14 @@
 // affects which of SafeBrowsingExtendedReporting or SafeBrowsingScoutReporting
 // is used.
 extern const char kSafeBrowsingScoutGroupSelected[];
+
+// Boolean indicating whether the user has ever seen a security interstitial
+// containing the legacy Extended Reporting opt-in.
+extern const char kSafeBrowsingSawInterstitialExtendedReporting[];
+
+// Boolean indicating whether the user has ever seen a security interstitial
+// containing the new Scout opt-in.
+extern const char kSafeBrowsingSawInterstitialScoutReporting[];
 }
 
 namespace safe_browsing {
diff --git a/components/safe_browsing_db/safe_browsing_prefs_unittest.cc b/components/safe_browsing_db/safe_browsing_prefs_unittest.cc
index af38c78..303f878 100644
--- a/components/safe_browsing_db/safe_browsing_prefs_unittest.cc
+++ b/components/safe_browsing_db/safe_browsing_prefs_unittest.cc
@@ -24,6 +24,10 @@
         prefs::kSafeBrowsingScoutReportingEnabled, false);
     prefs_.registry()->RegisterBooleanPref(
         prefs::kSafeBrowsingScoutGroupSelected, false);
+    prefs_.registry()->RegisterBooleanPref(
+        prefs::kSafeBrowsingSawInterstitialExtendedReporting, false);
+    prefs_.registry()->RegisterBooleanPref(
+        prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
 
     ResetExperiments(/*can_show_scout=*/false, /*only_show_scout=*/false);
   }
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
index 436e0d1..249c4e9 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
@@ -8,6 +8,7 @@
 import android.accounts.AuthenticatorDescription;
 import android.app.Activity;
 import android.support.annotation.AnyThread;
+import android.support.annotation.Nullable;
 import android.support.annotation.WorkerThread;
 
 import org.chromium.base.Callback;
@@ -80,5 +81,6 @@
      * @param callback The callback to indicate whether update is succeed or not.
      */
     @AnyThread
-    void updateCredentials(Account account, Activity activity, Callback<Boolean> callback);
+    void updateCredentials(
+            Account account, Activity activity, @Nullable Callback<Boolean> callback);
 }
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
index 0d71fa4..93745b6 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
@@ -9,6 +9,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.os.AsyncTask;
+import android.support.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
@@ -354,7 +355,8 @@
      * Asks the user to enter a new password for an account, updating the saved credentials for the
      * account.
      */
-    public void updateCredentials(Account account, Activity activity, Callback<Boolean> callback) {
+    public void updateCredentials(
+            Account account, Activity activity, @Nullable Callback<Boolean> callback) {
         mAccountManager.updateCredentials(account, activity, callback);
     }
 
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
index f26590e8..ebbc3dda 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -171,28 +171,26 @@
             return;
         }
 
-        mAccountManager.updateCredentials(
-                account, "android", null, activity, new AccountManagerCallback<Bundle>() {
-                    @Override
-                    public void run(AccountManagerFuture<Bundle> future) {
-                        assert future.isDone();
-                        Bundle bundle = null;
-                        try {
-                            bundle = future.getResult();
-                        } catch (AuthenticatorException | IOException e) {
-                            Log.e(TAG, "Error while update credentials: ", e);
-                        } catch (OperationCanceledException e) {
-                            Log.w(TAG, "Updating credentials was cancelled.");
-                        }
-                        if (bundle != null
-                                && bundle.getString(AccountManager.KEY_ACCOUNT_NAME) != null
-                                && bundle.getString(AccountManager.KEY_ACCOUNT_TYPE) != null) {
-                            callback.onResult(true);
-                        } else {
-                            callback.onResult(false);
-                        }
-                    }
-                }, null /* handler */);
+        AccountManagerCallback<Bundle> realCallback = new AccountManagerCallback<Bundle>() {
+            @Override
+            public void run(AccountManagerFuture<Bundle> future) {
+                Bundle bundle = null;
+                try {
+                    bundle = future.getResult();
+                } catch (AuthenticatorException | IOException e) {
+                    Log.e(TAG, "Error while update credentials: ", e);
+                } catch (OperationCanceledException e) {
+                    Log.w(TAG, "Updating credentials was cancelled.");
+                }
+                boolean success = bundle != null
+                        && bundle.getString(AccountManager.KEY_ACCOUNT_NAME) != null
+                        && bundle.getString(AccountManager.KEY_ACCOUNT_TYPE) != null;
+                if (callback != null) {
+                    callback.onResult(success);
+                }
+            }
+        };
+        mAccountManager.updateCredentials(account, "android", null, activity, realCallback, null);
     }
 
     protected boolean hasGetAccountsPermission() {
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
index d7a67c7..5d26d5e 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
@@ -184,6 +184,10 @@
     public void updateCredentials(
             Account account, Activity activity, final Callback<Boolean> callback) {
         ThreadUtils.assertOnUiThread();
+        if (callback == null) {
+            return;
+        }
+
         ThreadUtils.postOnUiThread(new Runnable() {
             @Override
             public void run() {
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 415f03d3..3076b95 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -58,6 +58,7 @@
     false,
 };
 
+// The maximum number of retries for a fetcher used in this class.
 const int kMaxFetcherRetries = 8;
 
 // Name of the GAIA cookie that is being observed to detect when available
@@ -131,6 +132,7 @@
 }
 
 void GaiaCookieManagerService::ExternalCcResultFetcher::Start() {
+  DCHECK(!helper_->external_cc_result_fetched_);
   m_external_cc_result_start_time_ = base::Time::Now();
 
   CleanupTransientState();
@@ -151,7 +153,8 @@
 }
 
 bool GaiaCookieManagerService::ExternalCcResultFetcher::IsRunning() {
-  return helper_->gaia_auth_fetcher_ || fetchers_.size() > 0u;
+  return helper_->gaia_auth_fetcher_ || fetchers_.size() > 0u ||
+         timer_.IsRunning();
 }
 
 void GaiaCookieManagerService::ExternalCcResultFetcher::TimeoutForTests() {
@@ -160,8 +163,6 @@
 
 void GaiaCookieManagerService::ExternalCcResultFetcher::
     OnGetCheckConnectionInfoSuccess(const std::string& data) {
-  helper_->fetcher_backoff_.InformOfRequest(true);
-  gaia_auth_fetcher_timer_.Stop();
   std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
   const base::ListValue* list;
   if (!value || !value->GetAsList(&list)) {
@@ -196,15 +197,17 @@
 
 void GaiaCookieManagerService::ExternalCcResultFetcher::
     OnGetCheckConnectionInfoError(const GoogleServiceAuthError& error) {
-  if (++helper_->fetcher_retries_ < kMaxFetcherRetries &&
-      error.IsTransientError()) {
-    helper_->fetcher_backoff_.InformOfRequest(false);
-    gaia_auth_fetcher_timer_.Start(
-        FROM_HERE, helper_->fetcher_backoff_.GetTimeUntilRelease(),
-        this, &GaiaCookieManagerService::ExternalCcResultFetcher::Start);
-    return;
-  }
+  VLOG(1) << "GaiaCookieManagerService::ExternalCcResultFetcher::"
+          << "OnGetCheckConnectionInfoError " << error.ToString();
 
+  // Chrome does not have any retry logic for fetching ExternalCcResult. The
+  // ExternalCcResult is only used to inform Gaia that Chrome has already
+  // checked the connection to other sites.
+  //
+  // In case fetching the ExternalCcResult fails:
+  // * The result of merging accounts to Gaia cookies will not be affected.
+  // * Gaia will need make its own call about whether to check them itself,
+  //   of make some other assumptions.
   CleanupTransientState();
   GetCheckConnectionInfoCompleted(false);
 }
@@ -257,6 +260,7 @@
 }
 
 void GaiaCookieManagerService::ExternalCcResultFetcher::Timeout() {
+  VLOG(1) << " GaiaCookieManagerService::ExternalCcResultFetcher::Timeout";
   CleanupTransientState();
   GetCheckConnectionInfoCompleted(false);
 }
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index ae8a8947..30d0562f 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -163,8 +163,6 @@
     ResultMap results_;
     base::Time m_external_cc_result_start_time_;
 
-    base::OneShotTimer gaia_auth_fetcher_timer_;
-
     DISALLOW_COPY_AND_ASSIGN(ExternalCcResultFetcher);
   };
 
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index c3c4429..238f45e 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -8,8 +8,8 @@
     "activation_state_computing_navigation_throttle.h",
     "async_document_subresource_filter.cc",
     "async_document_subresource_filter.h",
-    "content_ruleset_service_delegate.cc",
-    "content_ruleset_service_delegate.h",
+    "content_ruleset_service.cc",
+    "content_ruleset_service.h",
     "content_subresource_filter_driver_factory.cc",
     "content_subresource_filter_driver_factory.h",
     "subframe_navigation_filtering_throttle.cc",
@@ -39,7 +39,7 @@
   testonly = true
   sources = [
     "async_document_subresource_filter_unittest.cc",
-    "content_ruleset_service_delegate_unittest.cc",
+    "content_ruleset_service_unittest.cc",
     "content_subresource_filter_driver_factory_unittest.cc",
     "subframe_navigation_filtering_throttle_unittest.cc",
     "verified_ruleset_dealer_unittest.cc",
diff --git a/components/subresource_filter/content/browser/content_ruleset_service_delegate.cc b/components/subresource_filter/content/browser/content_ruleset_service.cc
similarity index 74%
rename from components/subresource_filter/content/browser/content_ruleset_service_delegate.cc
rename to components/subresource_filter/content/browser/content_ruleset_service.cc
index 079798aa..adcc930d 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service_delegate.cc
+++ b/components/subresource_filter/content/browser/content_ruleset_service.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 
 #include <utility>
 
 #include "base/logging.h"
 #include "base/macros.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
+#include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
@@ -42,7 +43,7 @@
 
 }  // namespace
 
-ContentRulesetServiceDelegate::ContentRulesetServiceDelegate() {
+ContentRulesetService::ContentRulesetService() {
   // Must rely on notifications as RenderProcessHostObserver::RenderProcessReady
   // would only be called after queued IPC messages (potentially triggering a
   // navigation) had already been sent to the new renderer.
@@ -51,24 +52,24 @@
       content::NotificationService::AllBrowserContextsAndSources());
 }
 
-ContentRulesetServiceDelegate::~ContentRulesetServiceDelegate() {
+ContentRulesetService::~ContentRulesetService() {
   CloseFileOnFileThread(&ruleset_data_);
 }
 
-void ContentRulesetServiceDelegate::SetRulesetPublishedCallbackForTesting(
+void ContentRulesetService::SetRulesetPublishedCallbackForTesting(
     base::Closure callback) {
   ruleset_published_callback_ = callback;
 }
 
-void ContentRulesetServiceDelegate::PostAfterStartupTask(base::Closure task) {
+void ContentRulesetService::PostAfterStartupTask(base::Closure task) {
   content::BrowserThread::PostAfterStartupTask(
-      FROM_HERE, content::BrowserThread::GetTaskRunnerForThread(
-                     content::BrowserThread::UI),
+      FROM_HERE,
+      content::BrowserThread::GetTaskRunnerForThread(
+          content::BrowserThread::UI),
       task);
 }
 
-void ContentRulesetServiceDelegate::PublishNewRulesetVersion(
-    base::File ruleset_data) {
+void ContentRulesetService::PublishNewRulesetVersion(base::File ruleset_data) {
   DCHECK(ruleset_data.IsValid());
   CloseFileOnFileThread(&ruleset_data_);
   ruleset_data_ = std::move(ruleset_data);
@@ -81,7 +82,19 @@
     ruleset_published_callback_.Run();
 }
 
-void ContentRulesetServiceDelegate::Observe(
+void ContentRulesetService::set_ruleset_service(
+    std::unique_ptr<RulesetService> ruleset_service) {
+  ruleset_service_ = std::move(ruleset_service);
+}
+
+void ContentRulesetService::IndexAndStoreAndPublishRulesetIfNeeded(
+    const UnindexedRulesetInfo& unindexed_ruleset_info) {
+  DCHECK(ruleset_service_);
+  ruleset_service_->IndexAndStoreAndPublishRulesetIfNeeded(
+      unindexed_ruleset_info);
+}
+
+void ContentRulesetService::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
diff --git a/components/subresource_filter/content/browser/content_ruleset_service_delegate.h b/components/subresource_filter/content/browser/content_ruleset_service.h
similarity index 78%
rename from components/subresource_filter/content/browser/content_ruleset_service_delegate.h
rename to components/subresource_filter/content/browser/content_ruleset_service.h
index ba5338c6..734e81d 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service_delegate.h
+++ b/components/subresource_filter/content/browser/content_ruleset_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_DELEGATE_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_DELEGATE_H_
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_H_
 
 #include "base/callback.h"
 #include "base/files/file.h"
@@ -14,7 +14,11 @@
 
 namespace subresource_filter {
 
-// The content-layer specific implementation of RulesetServiceDelegate.
+class RulesetService;
+struct UnindexedRulesetInfo;
+
+// The content-layer specific implementation of RulesetServiceDelegate. Owns the
+// underlying RulesetService.
 //
 // Its main responsibility is receiving new versions of subresource filtering
 // rules from the RulesetService, and distributing them to renderer processes,
@@ -42,11 +46,11 @@
 //
 // Note: UnverifiedRulesetDealer is shortened to *RulesetDealer above. There is
 // also a VerifiedRulesetDealer which is used similarly on the browser side.
-class ContentRulesetServiceDelegate : public RulesetServiceDelegate,
-                                      content::NotificationObserver {
+class ContentRulesetService : public RulesetServiceDelegate,
+                              content::NotificationObserver {
  public:
-  ContentRulesetServiceDelegate();
-  ~ContentRulesetServiceDelegate() override;
+  ContentRulesetService();
+  ~ContentRulesetService() override;
 
   void SetRulesetPublishedCallbackForTesting(base::Closure callback);
 
@@ -54,6 +58,12 @@
   void PostAfterStartupTask(base::Closure task) override;
   void PublishNewRulesetVersion(base::File ruleset_data) override;
 
+  void set_ruleset_service(std::unique_ptr<RulesetService> ruleset_service);
+
+  // Forwards calls to the underlying ruleset_service_.
+  void IndexAndStoreAndPublishRulesetIfNeeded(
+      const UnindexedRulesetInfo& unindex_ruleset_info);
+
  private:
   // content::NotificationObserver:
   void Observe(int type,
@@ -64,9 +74,11 @@
   base::File ruleset_data_;
   base::Closure ruleset_published_callback_;
 
-  DISALLOW_COPY_AND_ASSIGN(ContentRulesetServiceDelegate);
+  std::unique_ptr<RulesetService> ruleset_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentRulesetService);
 };
 
 }  // namespace subresource_filter
 
-#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_DELEGATE_H_
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_RULESET_SERVICE_H_
diff --git a/components/subresource_filter/content/browser/content_ruleset_service_delegate_unittest.cc b/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
similarity index 89%
rename from components/subresource_filter/content/browser/content_ruleset_service_delegate_unittest.cc
rename to components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
index 643835a..69a64ce3 100644
--- a/components/subresource_filter/content/browser/content_ruleset_service_delegate_unittest.cc
+++ b/components/subresource_filter/content/browser/content_ruleset_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
+#include "components/subresource_filter/content/browser/content_ruleset_service.h"
 
 #include <stddef.h>
 
@@ -94,10 +94,9 @@
 
 }  // namespace
 
-class SubresourceFilterContentRulesetServiceDelegateTest
-    : public ::testing::Test {
+class SubresourceFilterContentRulesetServiceTest : public ::testing::Test {
  public:
-  SubresourceFilterContentRulesetServiceDelegateTest()
+  SubresourceFilterContentRulesetServiceTest()
       : old_browser_client_(nullptr), existing_renderer_(&browser_context_) {}
 
  protected:
@@ -134,20 +133,19 @@
   content::TestBrowserContext browser_context_;
   NotifyingMockRenderProcessHost existing_renderer_;
 
-  DISALLOW_COPY_AND_ASSIGN(SubresourceFilterContentRulesetServiceDelegateTest);
+  DISALLOW_COPY_AND_ASSIGN(SubresourceFilterContentRulesetServiceTest);
 };
 
-TEST_F(SubresourceFilterContentRulesetServiceDelegateTest,
-       NoRuleset_NoIPCMessages) {
+TEST_F(SubresourceFilterContentRulesetServiceTest, NoRuleset_NoIPCMessages) {
   NotifyingMockRenderProcessHost existing_renderer(browser_context());
-  ContentRulesetServiceDelegate delegate;
+  ContentRulesetService service;
   NotifyingMockRenderProcessHost new_renderer(browser_context());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, existing_renderer.sink().message_count());
   EXPECT_EQ(0u, new_renderer.sink().message_count());
 }
 
-TEST_F(SubresourceFilterContentRulesetServiceDelegateTest,
+TEST_F(SubresourceFilterContentRulesetServiceTest,
        PublishedRuleset_IsDistributedToExistingAndNewRenderers) {
   const char kTestFileContents[] = "foobar";
   base::WriteFile(scoped_temp_file(), kTestFileContents,
@@ -158,12 +156,12 @@
                   base::File::FLAG_OPEN | base::File::FLAG_READ);
 
   NotifyingMockRenderProcessHost existing_renderer(browser_context());
-  ContentRulesetServiceDelegate delegate;
+  ContentRulesetService service;
   MockClosureTarget publish_callback_target;
-  delegate.SetRulesetPublishedCallbackForTesting(base::Bind(
+  service.SetRulesetPublishedCallbackForTesting(base::Bind(
       &MockClosureTarget::Call, base::Unretained(&publish_callback_target)));
   EXPECT_CALL(publish_callback_target, Call()).Times(1);
-  delegate.PublishNewRulesetVersion(std::move(file));
+  service.PublishNewRulesetVersion(std::move(file));
   base::RunLoop().RunUntilIdle();
   ::testing::Mock::VerifyAndClearExpectations(&publish_callback_target);
 
@@ -179,12 +177,11 @@
       second_renderer.sink().GetMessageAt(0), kTestFileContents));
 }
 
-TEST_F(SubresourceFilterContentRulesetServiceDelegateTest,
-       PostAfterStartupTask) {
-  ContentRulesetServiceDelegate delegate;
+TEST_F(SubresourceFilterContentRulesetServiceTest, PostAfterStartupTask) {
+  ContentRulesetService service;
 
   MockClosureTarget mock_closure_target;
-  delegate.PostAfterStartupTask(base::Bind(
+  service.PostAfterStartupTask(base::Bind(
       &MockClosureTarget::Call, base::Unretained(&mock_closure_target)));
 
   EXPECT_CALL(mock_closure_target, Call()).Times(1);
diff --git a/components/subresource_filter/content/renderer/unverified_ruleset_dealer.h b/components/subresource_filter/content/renderer/unverified_ruleset_dealer.h
index e46240a..329e612 100644
--- a/components/subresource_filter/content/renderer/unverified_ruleset_dealer.h
+++ b/components/subresource_filter/content/renderer/unverified_ruleset_dealer.h
@@ -24,7 +24,7 @@
 // sure that the file is valid.
 //
 // See RulesetDealerBase for details on the lifetime of MemoryMappedRuleset, and
-// the distribution pipeline diagram in content_ruleset_service_delegate.h.
+// the distribution pipeline diagram in content_ruleset_service.h.
 class UnverifiedRulesetDealer : public RulesetDealer,
                                 public content::RenderThreadObserver {
  public:
diff --git a/components/subresource_filter/core/browser/ruleset_service.cc b/components/subresource_filter/core/browser/ruleset_service.cc
index 179375b..4625672 100644
--- a/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/components/subresource_filter/core/browser/ruleset_service.cc
@@ -215,11 +215,11 @@
 RulesetService::RulesetService(
     PrefService* local_state,
     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
-    std::unique_ptr<RulesetServiceDelegate> delegate,
+    RulesetServiceDelegate* delegate,
     const base::FilePath& indexed_ruleset_base_dir)
     : local_state_(local_state),
       blocking_task_runner_(blocking_task_runner),
-      delegate_(std::move(delegate)),
+      delegate_(delegate),
       is_after_startup_(false),
       indexed_ruleset_base_dir_(indexed_ruleset_base_dir) {
   DCHECK(delegate_);
diff --git a/components/subresource_filter/core/browser/ruleset_service.h b/components/subresource_filter/core/browser/ruleset_service.h
index d460f14f..ef84c0ca 100644
--- a/components/subresource_filter/core/browser/ruleset_service.h
+++ b/components/subresource_filter/core/browser/ruleset_service.h
@@ -170,7 +170,7 @@
   // See class comments for details of arguments.
   RulesetService(PrefService* local_state,
                  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
-                 std::unique_ptr<RulesetServiceDelegate> delegate,
+                 RulesetServiceDelegate* delegate,
                  const base::FilePath& indexed_ruleset_base_dir);
   virtual ~RulesetService();
 
@@ -187,9 +187,6 @@
   virtual void IndexAndStoreAndPublishRulesetIfNeeded(
       const UnindexedRulesetInfo& unindexed_ruleset_info);
 
-  // Exposed for browser tests.
-  RulesetServiceDelegate* delegate() { return delegate_.get(); }
-
  private:
   friend class SubresourceFilteringRulesetServiceTest;
   FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceTest,
@@ -252,7 +249,9 @@
 
   PrefService* const local_state_;
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
-  std::unique_ptr<RulesetServiceDelegate> delegate_;
+
+  // Must outlive |this| object.
+  RulesetServiceDelegate* delegate_;
 
   UnindexedRulesetInfo queued_unindexed_ruleset_info_;
   bool is_after_startup_;
diff --git a/components/subresource_filter/core/browser/ruleset_service_unittest.cc b/components/subresource_filter/core/browser/ruleset_service_unittest.cc
index 0e300937..dccef008 100644
--- a/components/subresource_filter/core/browser/ruleset_service_unittest.cc
+++ b/components/subresource_filter/core/browser/ruleset_service_unittest.cc
@@ -145,8 +145,7 @@
  public:
   SubresourceFilteringRulesetServiceTest()
       : task_runner_(new base::TestSimpleTaskRunner),
-        task_runner_handle_(task_runner_),
-        mock_delegate_(nullptr) {}
+        task_runner_handle_(task_runner_) {}
 
  protected:
   void SetUp() override {
@@ -172,15 +171,14 @@
   }
 
   void ResetRulesetService() {
+    mock_delegate_ = base::MakeUnique<MockRulesetServiceDelegate>();
     service_ = base::MakeUnique<RulesetService>(
-        &pref_service_, task_runner_,
-        base::WrapUnique(mock_delegate_ = new MockRulesetServiceDelegate),
-        base_dir());
+        &pref_service_, task_runner_, mock_delegate_.get(), base_dir());
   }
 
   void ClearRulesetService() {
-    mock_delegate_ = nullptr;
     service_.reset();
+    mock_delegate_.reset();
   }
 
   // Creates a new file with the given license |contents| at a unique temporary
@@ -269,7 +267,7 @@
 
   PrefService* prefs() { return &pref_service_; }
   RulesetService* service() { return service_.get(); }
-  MockRulesetServiceDelegate* mock_delegate() { return mock_delegate_; }
+  MockRulesetServiceDelegate* mock_delegate() { return mock_delegate_.get(); }
 
   virtual base::FilePath effective_temp_dir() const {
     return scoped_temp_dir_.GetPath();
@@ -295,8 +293,8 @@
   TestRulesetPair test_ruleset_2_;
   TestRulesetPair test_ruleset_3_;
 
+  std::unique_ptr<MockRulesetServiceDelegate> mock_delegate_;
   std::unique_ptr<RulesetService> service_;
-  MockRulesetServiceDelegate* mock_delegate_;  // Weak, owned by |service_|.
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilteringRulesetServiceTest);
 };
diff --git a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
index 2f03ba0..a381ab8d 100644
--- a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
@@ -1,32 +1,134 @@
 /* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
+   Use of this source code is governed by a BSD-style license that can be
+   found in the LICENSE file. */
+
 body {
-  background-color: rgb(247, 247, 247);
-  font-size: 10pt;
-  margin: 150px 60px 0 30px;
+  background-color: #f7f7f7;
+  color: #646464;
 }
 
-#main-frame-blocked {
-  margin: auto;
-  max-width: 600px;
-  min-width: 200px;
+a {
+  color: #585858;
+}
+
+button {
+  border: 0;
+  border-radius: 2px;
+  box-sizing: border-box;
+  color: #fff;
+  cursor: pointer;
+  float: right;
+  font-size: .875em;
+  margin: 0;
+  padding: 10px 24px;
+  transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
+  user-select: none;
+}
+
+[dir='rtl'] button {
+  float: left;
+}
+
+.primary-button {
+  background: rgb(66, 133, 244);
+}
+
+.primary-button:active {
+  background: rgb(50, 102, 213);
+  outline: 0;
+}
+
+button:hover {
+  box-shadow: 0 1px 3px rgba(0, 0, 0, .50);
+}
+
+#details {
+  color: #696969;
+  margin: 45px 0 50px;
+}
+
+#details p:not(:first-of-type) {
+  margin-top: 20px;
+}
+
+#details-header {
+  font-weight: bold;
+}
+
+#details-button-container {
+  box-shadow: inherit;
+}
+
+.details-button {
+  background: inherit;
+  border: 0;
+  float: none;
+  margin: 0;
+  padding: 10px 0;
+  text-transform: uppercase;
+}
+
+.details-button:hover {
+  box-shadow: inherit;
+  text-decoration: underline;
 }
 
 h1 {
-  font-size: 1.8em;
+  color: #333;
+  font-size: 1.6em;
   font-weight: normal;
-  margin: 5px 0 25px 0;
+  line-height: 1.25em;
+  margin-bottom: 16px;
 }
 
-#block-page-message {
-  color: rgb(97,97,97);
-  font-size: 0.667em;
-  margin-top: 16px;
+h2 {
+  font-size: 1.2em;
+  font-weight: normal;
+}
+
+html {
+  -webkit-text-size-adjust: 100%;
+  font-size: 125%;
+}
+
+.icon {
+  background-repeat: no-repeat;
+  background-size: 100%;
+  height: 72px;
+  margin: 0 0 40px;
+  width: 72px;
+}
+
+.main-frame-blocked {
+  box-sizing: border-box;
+  font-size: 1em;
+  line-height: 1.6em;
+  margin: 100px auto 0;
+  max-width: 600px;
+  width: 100%;
+}
+
+#main-message > p:not([hidden]) {
+  display: inline;
+}
+
+.button-container {
+  margin-top: 51px;
+}
+
+.button-container::after {
+  clear: both;
+  content: '';
+  display: table;
+  width: 100%;
+}
+
+.small-link {
+  color: #696969;
+  font-size: .875em;
 }
 
 .avatar-img {
-  -webkit-user-select: none;
   border: 3px solid rgb(251, 251, 251);
   border-radius: 50%;
   content: -webkit-image-set(
@@ -37,61 +139,13 @@
   margin-top: 5px;
   max-width: 45px;
   position: relative;
+  user-select: none;
 }
 
 #feedback {
   margin-top: 50px;
 }
 
-#feedback-link {
-  color: rgb(66, 133, 244);
-}
-
-#request-access-button {
-  background-color: rgb(66, 133, 244);
-  color: rgb(255, 255, 255);
-  cursor: pointer;
-  font-size: 12px;
-  font-weight: bold;
-  min-width: 88px;
-  padding: 10px 15px;
-  transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
-  transition-delay: 200ms;
-}
-
-#request-access-button:hover {
-  background-color: rgb(30, 136, 229);
-}
-
-#request-access-button:active {
-  background-color: rgb(25,118,210);
-  box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
-  transition-delay: 0s;
-}
-
-#details-button-container {
-  color: rgb(97,97,97);
-  cursor: pointer;
-  display: inline;
-  font-size: 12px;
-}
-
-#button-container {
-  align-items: baseline;
-  display: flex;
-  justify-content: space-between;
-  margin-top: 60px;
-}
-
-#details {
-  color: rgb(97,97,97);
-  font-size: 14px;
-}
-
-#details-header {
-  font-weight: bold;
-}
-
 .custodian-information {
   align-items: center;
   display: flex;
@@ -108,46 +162,356 @@
   padding: 1px 0;
 }
 
-@media (max-width: 600px) {
-  #button-container {
-    display: flex;
-    flex-flow: column;
-    justify-content: flex-start;
-    order: 2;
+
+@media (max-width: 700px) {
+  .main-frame-blocked {
+    padding: 0 10%;
+  }
+}
+
+@media (max-height: 600px) {
+  #feedback {
+    margin-top: 35px;
+  }
+}
+
+@media (max-width: 420px) {
+  button,
+  [dir='rtl'] button,
+  .small-link {
+    float: none;
+    font-size: .825em;
+    font-weight: 400;
+    margin: 0;
     text-transform: uppercase;
-  }
-
-  #details-button-container {
-    font-weight: bold;
-    margin: auto;
-    order: 2;
-    padding: 0 15px;
-    text-align: center;
-    width: 100%;
-  }
-
-  #request-access-button {
-    margin-bottom: 30px;
-    order: 1;
-    text-align: center;
     width: 100%;
   }
 
   #details {
-    margin: auto;
-    order: 1;
+    margin: 20px 0 20px 0;
   }
 
-  .hidden-on-mobile {
-    display: none;
+  #details p:not(:first-of-type) {
+    margin-top: 10px;
   }
 
-  #main-frame-blocked {
+  .details-button {
+    margin-top: 20px;
+    text-align: center;
+    width: 100%;
+  }
+
+  .main-frame-blocked {
+    padding: 0 5%;
+  }
+
+  .button-container {
+    margin-top: 30px;
+  }
+}
+
+/**
+ * Mobile specific styling.
+ * Navigation buttons are anchored to the bottom of the screen.
+ * Details message replaces the top content in its own scrollable area.
+ */
+
+@media (max-width: 420px) and (max-height: 736px) and (orientation: portrait) {
+  .details-button {
+    border: 0;
+    margin: 8px 0 0;
+  }
+
+  .secondary-button {
+    -webkit-margin-end: 0;
+    margin-top: 16px;
+  }
+}
+
+/* Fixed nav. */
+@media (min-width: 240px) and (max-width: 420px) and
+       (min-height: 401px) and (max-height: 736px) and (orientation:portrait),
+       (min-width: 421px) and (max-width: 736px) and (min-height: 240px) and
+       (max-height: 420px) and (orientation:landscape) {
+  body .button-container {
+    background: #f7f7f7;
+    bottom: 0;
+    box-shadow: 0 -22px 40px rgb(247, 247, 247);
+    left: 0;
+    margin: 0;
+    max-width: 736px;
+    padding-left: 24px;
+    padding-right: 24px;
+    position: fixed;
+    z-index: 2;
+  }
+
+  .main-frame-blocked {
+    max-width: 736px;
+  }
+
+  #details,
+  #information-container {
+    padding-bottom: 40px;
+  }
+}
+
+@media (max-width: 420px) and (max-height: 736px) and (orientation: portrait),
+       (max-width: 736px) and (max-height: 420px) and (orientation: landscape) {
+  body {
+    margin: 0 auto;
+  }
+
+  button,
+  [dir='rtl'] button,
+  button.small-link {
+    font-family: Roboto-Regular,Helvetica;
+    font-size: .933em;
+    font-weight: 600;
+    margin: 6px 0;
+    text-transform: uppercase;
+    transform: translatez(0);
+  }
+
+  .button-container {
+    box-sizing: border-box;
+    padding-bottom: 8px;
+    width: 100%;
+  }
+
+  #details {
+    box-sizing: border-box;
+    height: auto;
+    margin: 0;
+    opacity: 1;
+    padding-top: 40px;
+    transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  #details[hidden],
+  #information-container[hidden] {
+    display: block;
+    height: 0;
+    opacity: 0;
+    overflow: hidden;
+    transition: none;
+  }
+
+  .details-button {
+    padding-bottom: 16px;
+    padding-top: 16px;
+  }
+
+  h1 {
+    font-size: 1.5em;
+    margin-bottom: 8px;
+  }
+
+  .icon {
+    margin-bottom: 12px;
+  }
+
+  .main-frame-blocked {
+    box-sizing: border-box;
+    margin: 24px auto 12px;
+    padding: 0 24px;
+    position: relative;
+  }
+
+  .main-frame-blocked p {
+    font-size: .95em;
+    line-height: 1.61em;
+    margin-top: 8px;
+  }
+
+  #information-container {
+    margin: 0;
+    transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .small-link {
+    border: 0;
+  }
+}
+
+@media (min-height: 400px) and (orientation:portrait) {
+  .main-frame-blocked {
+    margin-bottom: 145px;
+  }
+}
+
+@media (min-height: 299px) and (orientation:portrait) {
+  .button-container {
+    padding-bottom: 16px;
+  }
+}
+
+@media (min-height: 405px) and (max-height: 736px) and
+       (max-width: 420px) and (orientation:portrait) {
+  .icon {
+    margin-bottom: 24px;
+  }
+
+  .main-frame-blocked {
+    margin-top: 64px;
+  }
+}
+
+@media (min-height: 480px) and (max-width: 420px) and
+       (max-height: 736px) and (orientation: portrait),
+       (min-height: 338px) and (max-height: 420px) and (max-width: 736px) and
+       (orientation: landscape) {
+  .icon {
+    margin-bottom: 24px;
+  }
+
+  .button-container {
+    padding-bottom: 24px;
+  }
+}
+
+@media (min-height: 500px) and (max-width: 414px) and (orientation: portrait) {
+  .main-frame-blocked {
+    margin-top: 96px;
+  }
+}
+
+/* Phablet sizing */
+@media (min-width: 375px) and (min-height: 641px) and (max-height: 736px) and
+       (max-width: 414px) and (orientation: portrait) {
+  button,
+  [dir='rtl'] button,
+  .small-link {
+    font-size: 1em;
+    padding-bottom: 12px;
+    padding-top: 12px;
+  }
+
+  body:not(.offline) .icon {
+    height: 80px;
+    width: 80px;
+  }
+
+  .details-button {
+    margin-top: 28px;
+  }
+
+  h1 {
+    font-size: 1.7em;
+  }
+
+  .icon {
+    margin-bottom: 28px;
+  }
+
+  .main-frame-blocked {
+    padding: 28px;
+  }
+
+  .main-frame-blocked p {
+    font-size: 1.05em;
+  }
+
+  .button-container {
+    padding: 28px;
+  }
+}
+
+@media (min-width: 420px) and (max-width: 736px) and
+       (min-height: 240px) and (max-height: 298px) and
+       (orientation:landscape) {
+  body:not(.offline) .icon {
+    height: 50px;
+    width: 50px;
+  }
+
+  .icon {
+    padding-top: 0;
+  }
+
+  .main-frame-blocked {
+    margin-top: 16px;
+  }
+
+  .button-container {
+    padding: 0 24px 8px;
+  }
+}
+
+@media (min-width: 420px) and (max-width: 736px) and
+       (min-height: 240px) and (max-height: 420px) and
+       (orientation:landscape) {
+  .details-button {
+    margin: 0;
+  }
+
+  .main-frame-blocked {
+    margin-bottom: 70px;
+  }
+
+  .button-container {
+    margin-top: 0;
+  }
+}
+
+/* Phablet landscape */
+@media (min-width: 680px) and (max-height: 414px) {
+  .main-frame-blocked {
+    margin: 24px auto;
+  }
+
+  .button-container {
+    margin: 16px auto 0;
+  }
+}
+
+@media (max-height: 240px) and (orientation: landscape),
+       (max-height: 480px) and (orientation: portrait),
+       (max-width: 419px) and (max-height: 323px) {
+  body:not(.offline) .icon {
+    height: 56px;
+    width: 56px;
+  }
+
+  .icon {
+    margin-bottom: 16px;
+  }
+}
+
+/* Small mobile screens. No fixed nav. */
+@media (max-height: 400px) and (orientation: portrait),
+       (max-height: 239px) and (orientation: landscape),
+       (max-width: 419px) and (max-height: 399px) {
+  .main-frame-blocked {
     display: flex;
-    flex-flow: column;
+    flex-direction: column;
+    margin-bottom: 0;
   }
 
-  #feedback {
-    margin-top: 35px;
+  #details {
+    flex: 1 1 auto;
+    order: 0;
+  }
+
+  #information-container {
+    flex: 1 1 auto;
+    order: 0;
+  }
+
+  .button-container {
+    flex: 0 1 auto;
+    margin-top: 8px;
+    order: 1;
+    padding-left: 0;
+    padding-right: 0;
+    position: relative;
+    width: 100%;
+  }
+}
+
+@media (max-width: 239px) and (orientation: portrait) {
+  .button-container {
+    padding-left: 0;
+    padding-right: 0;
   }
 }
diff --git a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
index 69142c2..2a7de4b 100644
--- a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
@@ -1,27 +1,26 @@
 <!doctype html>
 <html i18n-values="dir:textdirection;lang:language">
 <head>
-<meta name="viewport" content="width=device-width">
 <meta charset="utf-8">
+<meta name="viewport"
+    content="initial-scale=1, minimum-scale=1, width=device-width">
 <title i18n-content="blockPageTitle"></title>
-<link rel="stylesheet" href="../../../ui/webui/resources/css/widgets.css">
 <link rel="stylesheet" href="supervised_user_block_interstitial.css">
 <script src="../../../ui/webui/resources/js/cr.js"></script>
 <script src="../../../ui/webui/resources/js/util.js"></script>
 <script src="supervised_user_block_interstitial.js"></script>
 </head>
-
 <body>
-<div id="main-frame-blocked">
+<div class="main-frame-blocked">
   <div id="information-container">
-    <h1>
-      <div id="block-page-header" i18n-content="blockPageHeader"></div>
-      <div id="block-page-message" i18n-content="blockPageMessage"></div>
-      <div id="request-failed-message" i18n-content="requestFailedMessage"
-          hidden></div>
-      <div id="request-sent-message" i18n-content="requestSentMessage" hidden>
-      </div>
-    </h1>
+    <div class="icon" id="icon"></div>
+    <div id="main-message">
+      <h1 id="block-page-header" i18n-content="blockPageHeader"></h1>
+      <p id="block-page-message" i18n-content="blockPageMessage"></p>
+      <h1 id="request-failed-message" i18n-content="requestFailedMessage"
+          hidden></h1>
+      <h1 id="request-sent-message" i18n-content="requestSentMessage" hidden></h1>
+    </div>
     <div id="custodians-information" hidden>
       <div id="custodian-information" class="custodian-information">
         <img id="custodian-avatar-img" class="avatar-img">
@@ -39,19 +38,14 @@
       </div>
     </div>
   </div>
-  <div id="button-container">
+  <div class="button-container">
+    <button id="request-access-button" class="primary-button" i18n-content="requestAccessButton">
+    </button>
     <div id="details-button-container">
-      <a id="show-details-link" i18n-content="showDetailsLink"
-          hidden class="button">
-      </a>
-      <a id="hide-details-link" i18n-content="hideDetailsLink"
-          hidden class="button"></a>
-      <a id="back-button" i18n-content="backButton"
-          hidden class="button"></a>
+      <button id="show-details-link" class="details-button small-link" i18n-content="showDetailsLink" hidden></button>
+      <button id="hide-details-link" class="details-button small-link" i18n-content="hideDetailsLink" hidden></button>
+      <button id="back-button" class="details-button small-link" i18n-content="backButton" hidden></button>
     </div>
-    <div id="request-access-button" class="button"
-        i18n-content="requestAccessButton">
-    </div> 
   </div>
   <div id="details" hidden>
     <p id="details-header" i18n-content="blockReasonHeader"></p>
diff --git a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
index 18a46c8..6b9abf4 100644
--- a/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
@@ -2,6 +2,40 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var mobileNav = false;
+
+var showDetails = false;
+
+/**
+ * For small screen mobile the navigation buttons are moved
+ * below the advanced text.
+ */
+function onResize() {
+  var mediaQuery = '(min-width: 240px) and (max-width: 420px) and ' +
+      '(max-height: 736px) and (min-height: 401px) and ' +
+      '(orientation: portrait), (max-width: 736px) and ' +
+      '(max-height: 420px) and (min-height: 240px) and ' +
+      '(min-width: 421px) and (orientation: landscape)';
+
+  // Check for change in nav status.
+  if (mobileNav != window.matchMedia(mediaQuery).matches) {
+    mobileNav = !mobileNav;
+    updateDetails();
+  }
+}
+
+function updateDetails() {
+  $('information-container').hidden = mobileNav && showDetails;
+  $('details').hidden = !showDetails;
+}
+
+function setupMobileNav() {
+  window.addEventListener('resize', onResize);
+  onResize();
+}
+
+document.addEventListener('DOMContentLoaded', setupMobileNav);
+
 function sendCommand(cmd) {
   window.domAutomationController.setAutomationId(1);
   window.domAutomationController.send(cmd);
@@ -57,18 +91,16 @@
     sendCommand('back');
   };
   $('show-details-link').onclick = function(event) {
-    $('details').hidden = false;
+    showDetails = true;
     $('show-details-link').hidden = true;
     $('hide-details-link').hidden = false;
-    $('information-container').classList.add('hidden-on-mobile');
-    $('request-access-button').classList.add('hidden-on-mobile');
+    updateDetails();
   };
   $('hide-details-link').onclick = function(event) {
-    $('details').hidden = true;
+    showDetails = false;
     $('show-details-link').hidden = false;
     $('hide-details-link').hidden = true;
-    $('information-container').classList.remove('hidden-on-mobile');
-    $('request-access-button').classList.remove('hidden-on-mobile');
+    updateDetails();
   };
   if (window.domAutomationController &&
         loadTimeData.getBoolean('showFeedbackLink')) {
@@ -88,17 +120,20 @@
   console.log('setRequestStatus(' + isSuccessful +')');
   $('block-page-header').hidden = true;
   $('block-page-message').hidden = true;
+  $('hide-details-link').hidden = true;
+  showDetails = false;
+  updateDetails();
+
   if (isSuccessful) {
     $('request-failed-message').hidden = true;
     $('request-sent-message').hidden = false;
-    $('show-details-link').hidden = true;
-    $('hide-details-link').hidden = true;
-    $('details').hidden = true;
     $('back-button').hidden = !window.domAutomationController;
     $('request-access-button').hidden = true;
+    $('show-details-link').hidden = true;
   } else {
     $('request-failed-message').hidden = false;
     $('request-access-button').hidden = false;
+    $('show-details-link').hidden = false;
   }
 }
 
diff --git a/components/supervised_user_error_page/supervised_user_error_page.cc b/components/supervised_user_error_page/supervised_user_error_page.cc
index 308996d76..e098c062 100644
--- a/components/supervised_user_error_page/supervised_user_error_page.cc
+++ b/components/supervised_user_error_page/supervised_user_error_page.cc
@@ -23,11 +23,9 @@
 static const int kAvatarSize1x = 45;
 static const int kAvatarSize2x = 90;
 
-#if defined(GOOGLE_CHROME_BUILD)
 bool ReasonIsAutomatic(FilteringBehaviorReason reason) {
   return reason == ASYNC_CHECKER || reason == BLACKLIST;
 }
-#endif
 
 std::string BuildAvatarImageUrl(const std::string& url, int size) {
   std::string result = url;
@@ -124,10 +122,9 @@
                         reason, is_child_account, second_custodian.empty())));
   strings.SetString("blockReasonHeader", l10n_util::GetStringUTF16(
                                              IDS_SUPERVISED_USER_BLOCK_HEADER));
-  bool show_feedback = false;
-#if defined(GOOGLE_CHROME_BUILD)
-  show_feedback = is_child_account && ReasonIsAutomatic(reason);
-#endif
+  bool show_feedback = ReasonIsAutomatic(reason);
+  DCHECK(is_child_account || !show_feedback);
+
   strings.SetBoolean("showFeedbackLink", show_feedback);
   strings.SetString("feedbackLink", l10n_util::GetStringUTF16(
                                         IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK));
diff --git a/components/supervised_user_error_page/supervised_user_error_page_unittest.cc b/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
index fbd5e2e1..7565ff4 100644
--- a/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
+++ b/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
@@ -40,8 +40,7 @@
     {DEFAULT, false, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT},
     {DEFAULT, true, true, IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT},
     {DEFAULT, true, false, IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT},
-    {ASYNC_CHECKER, false, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
-    {ASYNC_CHECKER, false, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
+    // SafeSites is not enabled for supervised users.
     {ASYNC_CHECKER, true, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
     {ASYNC_CHECKER, true, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
     {MANUAL, false, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL},
@@ -93,13 +92,12 @@
     EXPECT_THAT(result, testing::HasSubstr(param.second_custodian));
     EXPECT_THAT(result, testing::HasSubstr(param.second_custodian_email));
   }
-#if defined(GOOGLE_CHROME_BUILD)
-  if (param.is_child_account &&
-      (param.reason == ASYNC_CHECKER || param.reason == BLACKLIST))
+  if (param.reason == ASYNC_CHECKER || param.reason == BLACKLIST) {
     EXPECT_THAT(result, testing::HasSubstr("\"showFeedbackLink\":true"));
-  else
-#endif
+  } else {
     EXPECT_THAT(result, testing::HasSubstr("\"showFeedbackLink\":false"));
+  }
+
   // Messages containing parameters aren't tested since they get modified before
   // they are added to the result.
   if (param.allow_access_requests) {
@@ -201,7 +199,7 @@
     {true, "url1", "url2", "custodian", "custodian_email", "custodian2",
      "custodian2_email", false, DEFAULT, true},
     {true, "url1", "url2", "custodian", "custodian_email", "custodian2",
-     "custodian2_email", false, ASYNC_CHECKER, true},
+     "custodian2_email", true, ASYNC_CHECKER, true},
 };
 
 INSTANTIATE_TEST_CASE_P(GetBlockMessageIDParameterized,
diff --git a/components/supervised_user_error_page_strings.grdp b/components/supervised_user_error_page_strings.grdp
index de4c44a1..264f4d6 100644
--- a/components/supervised_user_error_page_strings.grdp
+++ b/components/supervised_user_error_page_strings.grdp
@@ -44,7 +44,7 @@
         We could not reach your parents at the moment. Please try again.
       </message>
       <message name="IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK" desc="The text for a link to submit feedback about a blocked site.">
-        Was this unexpected? <ph name="BEGIN_LINK">&lt;a is="action-link" id="feedback-link"&gt;</ph>Let us know<ph name="END_LINK">&lt;/a&gt;</ph>
+        Was this unexpected? <ph name="BEGIN_LINK">&lt;a href="#" id="feedback-link"&gt;</ph>Let us know<ph name="END_LINK">&lt;/a&gt;</ph>
       </message>
       <message name="IDS_BLOCK_INTERSTITIAL_SHOW_DETAILS" desc="The text for the link to show details about the interstitial.">
         Details
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index c7a6b2c..2cf1d98 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "content/browser/notifications/blink_notification_service_impl.h"
 #include "content/browser/notifications/notification_database.h"
@@ -131,13 +132,11 @@
 void PlatformNotificationContextImpl::RemoveService(
     BlinkNotificationServiceImpl* service) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  auto services_to_remove = std::remove_if(
-      services_.begin(), services_.end(),
+  base::EraseIf(
+      services_,
       [service](const std::unique_ptr<BlinkNotificationServiceImpl>& ptr) {
         return ptr.get() == service;
       });
-
-  services_.erase(services_to_remove, services_.end());
 }
 
 void PlatformNotificationContextImpl::ReadNotificationData(
diff --git a/content/browser/renderer_host/text_input_manager.cc b/content/browser/renderer_host/text_input_manager.cc
index c472b34c..6a96396 100644
--- a/content/browser/renderer_host/text_input_manager.cc
+++ b/content/browser/renderer_host/text_input_manager.cc
@@ -102,7 +102,11 @@
     // already synthesized the loss of TextInputState for the |view| before (see
     // below). So we can forget about this method ever being called (no observer
     // calls necessary).
+    // NOTE: Android requires state to be returned even when the current state
+    // is/becomes NONE. Otherwise IME may become irresponsive.
+#if !defined(OS_ANDROID)
     return;
+#endif
   }
 
   // Since |view| is registered, we already have a previous value for its
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f8910b4..8647047 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -173,7 +173,7 @@
 const char kDotGoogleDotCom[] = ".google.com";
 
 #if defined(OS_ANDROID)
-const char kWebContentsAndroidKey[] = "web_contents_android";
+const void* const kWebContentsAndroidKey = &kWebContentsAndroidKey;
 #endif  // OS_ANDROID
 
 base::LazyInstance<std::vector<WebContentsImpl::CreatedCallback>>::
diff --git a/content/child/service_worker/service_worker_network_provider.cc b/content/child/service_worker/service_worker_network_provider.cc
index 555f509..7a13d72 100644
--- a/content/child/service_worker/service_worker_network_provider.cc
+++ b/content/child/service_worker/service_worker_network_provider.cc
@@ -6,6 +6,8 @@
 
 #include "base/atomic_sequence_num.h"
 #include "content/child/child_thread_impl.h"
+#include "content/child/request_extra_data.h"
+#include "content/child/service_worker/service_worker_handle_reference.h"
 #include "content/child/service_worker/service_worker_provider_context.h"
 #include "content/common/navigation_params.h"
 #include "content/common/service_worker/service_worker_messages.h"
@@ -14,6 +16,7 @@
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "ipc/ipc_sync_channel.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
 
@@ -21,8 +24,6 @@
 
 namespace {
 
-const char kUserDataKey[] = "SWProviderKey";
-
 // Must be unique in the child process.
 int GetNextProviderId() {
   static base::StaticAtomicSequenceNumber sequence;
@@ -42,22 +43,44 @@
   return true;
 }
 
+// An WebServiceWorkerNetworkProvider for frame. This wraps
+// ServiceWorkerNetworkProvider implementation and is owned by blink.
+class WebServiceWorkerNetworkProviderForFrame
+    : public blink::WebServiceWorkerNetworkProvider {
+ public:
+  WebServiceWorkerNetworkProviderForFrame(
+      std::unique_ptr<ServiceWorkerNetworkProvider> provider)
+      : provider_(std::move(provider)) {}
+
+  void willSendRequest(blink::WebURLRequest& request) override {
+    RequestExtraData* extra_data =
+        static_cast<RequestExtraData*>(request.getExtraData());
+    if (!extra_data)
+      extra_data = new RequestExtraData();
+    extra_data->set_service_worker_provider_id(provider_->provider_id());
+    request.setExtraData(extra_data);
+  }
+
+  bool isControlledByServiceWorker() override {
+    return provider_->IsControlledByServiceWorker();
+  }
+
+  int64_t serviceWorkerID() override {
+    if (provider_->context() && provider_->context()->controller())
+      return provider_->context()->controller()->version_id();
+    return kInvalidServiceWorkerVersionId;
+  }
+
+  ServiceWorkerNetworkProvider* provider() { return provider_.get(); }
+
+ private:
+  std::unique_ptr<ServiceWorkerNetworkProvider> provider_;
+};
+
 }  // namespace
 
-void ServiceWorkerNetworkProvider::AttachToDocumentState(
-    base::SupportsUserData* datasource_userdata,
-    std::unique_ptr<ServiceWorkerNetworkProvider> network_provider) {
-  datasource_userdata->SetUserData(&kUserDataKey, network_provider.release());
-}
-
-ServiceWorkerNetworkProvider* ServiceWorkerNetworkProvider::FromDocumentState(
-    base::SupportsUserData* datasource_userdata) {
-  return static_cast<ServiceWorkerNetworkProvider*>(
-      datasource_userdata->GetUserData(&kUserDataKey));
-}
-
 // static
-std::unique_ptr<ServiceWorkerNetworkProvider>
+std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
 ServiceWorkerNetworkProvider::CreateForNavigation(
     int route_id,
     const RequestNavigationParams& request_params,
@@ -114,7 +137,16 @@
     network_provider = std::unique_ptr<ServiceWorkerNetworkProvider>(
         new ServiceWorkerNetworkProvider());
   }
-  return network_provider;
+  return base::MakeUnique<WebServiceWorkerNetworkProviderForFrame>(
+      std::move(network_provider));
+}
+
+// static
+ServiceWorkerNetworkProvider*
+ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
+    blink::WebServiceWorkerNetworkProvider* provider) {
+  return static_cast<WebServiceWorkerNetworkProviderForFrame*>(provider)
+      ->provider();
 }
 
 ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider(
diff --git a/content/child/service_worker/service_worker_network_provider.h b/content/child/service_worker/service_worker_network_provider.h
index c991aec..94afa943 100644
--- a/content/child/service_worker/service_worker_network_provider.h
+++ b/content/child/service_worker/service_worker_network_provider.h
@@ -18,12 +18,13 @@
 
 namespace blink {
 class WebLocalFrame;
+class WebServiceWorkerNetworkProvider;
 }  // namespace blink
 
 namespace content {
 
-class ServiceWorkerProviderContext;
 struct RequestNavigationParams;
+class ServiceWorkerProviderContext;
 
 // A unique provider_id is generated for each instance.
 // Instantiated prior to the main resource load being started and remains
@@ -35,22 +36,20 @@
 //
 // Basically, it's a scoped integer that sends an ipc upon construction
 // and destruction.
-class CONTENT_EXPORT ServiceWorkerNetworkProvider
-    : public base::SupportsUserData::Data {
+class CONTENT_EXPORT ServiceWorkerNetworkProvider {
  public:
-  // Ownership is transferred to the DocumentState.
-  static void AttachToDocumentState(
-      base::SupportsUserData* document_state,
-      std::unique_ptr<ServiceWorkerNetworkProvider> network_provider);
+  // Creates a ServiceWorkerNetworkProvider for navigation and wraps it
+  // with WebServiceWorkerNetworkProvider to be owned by Blink.
+  static std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
+  CreateForNavigation(int route_id,
+                      const RequestNavigationParams& request_params,
+                      blink::WebLocalFrame* frame,
+                      bool content_initiated);
 
-  static ServiceWorkerNetworkProvider* FromDocumentState(
-      base::SupportsUserData* document_state);
-
-  static std::unique_ptr<ServiceWorkerNetworkProvider> CreateForNavigation(
-      int route_id,
-      const RequestNavigationParams& request_params,
-      blink::WebLocalFrame* frame,
-      bool content_initiated);
+  // Valid only for WebServiceWorkerNetworkProvider created by
+  // CreateForNavigation.
+  static ServiceWorkerNetworkProvider* FromWebServiceWorkerNetworkProvider(
+      blink::WebServiceWorkerNetworkProvider*);
 
   // PlzNavigate
   // The |browser_provider_id| is initialized by the browser for navigations.
@@ -61,8 +60,9 @@
   ServiceWorkerNetworkProvider(int route_id,
                                ServiceWorkerProviderType type,
                                bool is_parent_frame_secure);
+
   ServiceWorkerNetworkProvider();
-  ~ServiceWorkerNetworkProvider() override;
+  ~ServiceWorkerNetworkProvider();
 
   int provider_id() const { return provider_id_; }
   ServiceWorkerProviderContext* context() const { return context_.get(); }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index b22367f..35bd096 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -2416,7 +2416,8 @@
     /**
      * @see View#onDragEvent(DragEvent)
      */
-    @TargetApi(Build.VERSION_CODES.N)
+    // TODO(hush): uncomment below when we build with API 24.
+    // @TargetApi(Build.VERSION_CODES.N)
     public boolean onDragEvent(DragEvent event) {
         if (mNativeContentViewCore == 0 || Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
             return false;
@@ -2436,8 +2437,9 @@
 
         StringBuilder content = new StringBuilder("");
         if (event.getAction() == DragEvent.ACTION_DROP) {
-            // TODO(hush): obtain dragdrop permissions, when dragging files into Chrome/WebView is
-            // supported. Not necessary to do so for now, because only text dragging is supported.
+            // TODO(hush): obtain dragdrop permissions (via reflection?), when dragging files into
+            // Chrome/WebView is supported. Not necessary to do so for now, because only text
+            // dragging is supported.
             ClipData clipData = event.getClipData();
             final int itemCount = clipData.getItemCount();
             for (int i = 0; i < itemCount; i++) {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index e3199573..5a7b201 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -187,6 +187,7 @@
 #include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h"
 #include "third_party/WebKit/public/web/WebColorSuggestion.h"
 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
@@ -2987,16 +2988,14 @@
   if (!ChildThreadImpl::current())
     return nullptr;  // May be null in some tests.
   ServiceWorkerNetworkProvider* provider =
-      ServiceWorkerNetworkProvider::FromDocumentState(
-          DocumentState::FromDataSource(frame_->dataSource()));
-  DCHECK(provider);
+      ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
+          frame_->dataSource()->getServiceWorkerNetworkProvider());
   if (!provider->context()) {
     // The context can be null when the frame is sandboxed.
     return nullptr;
   }
   return new WebServiceWorkerProviderImpl(
-      ChildThreadImpl::current()->thread_safe_sender(),
-      provider->context());
+      ChildThreadImpl::current()->thread_safe_sender(), provider->context());
 }
 
 void RenderFrameImpl::didAccessInitialDocument() {
@@ -3418,12 +3417,10 @@
   // Create the serviceworker's per-document network observing object if it
   // does not exist (When navigation happens within a page, the provider already
   // exists).
-  if (ServiceWorkerNetworkProvider::FromDocumentState(
-          DocumentState::FromDataSource(datasource)))
+  if (datasource->getServiceWorkerNetworkProvider())
     return;
 
-  ServiceWorkerNetworkProvider::AttachToDocumentState(
-      DocumentState::FromDataSource(datasource),
+  datasource->setServiceWorkerNetworkProvider(
       ServiceWorkerNetworkProvider::CreateForNavigation(
           routing_id_, navigation_state->request_params(), frame,
           content_initiated));
@@ -4280,31 +4277,30 @@
   // when it is re-created in the new process.
   bool should_replace_current_entry = data_source->replacesCurrentHistoryItem();
 
-  int provider_id = kInvalidServiceWorkerProviderId;
+  // Initializes service worker related request info.
   if (request.getFrameType() == blink::WebURLRequest::FrameTypeTopLevel ||
       request.getFrameType() == blink::WebURLRequest::FrameTypeNested) {
     // |provisionalDataSource| may be null in some content::ResourceFetcher
     // use cases, we don't hook those requests.
     if (frame->provisionalDataSource()) {
-      ServiceWorkerNetworkProvider* provider =
-          ServiceWorkerNetworkProvider::FromDocumentState(
-              DocumentState::FromDataSource(frame->provisionalDataSource()));
+      blink::WebServiceWorkerNetworkProvider* provider =
+          frame->provisionalDataSource()->getServiceWorkerNetworkProvider();
       DCHECK(provider);
-      provider_id = provider->provider_id();
+      provider->willSendRequest(request);
     }
   } else if (frame->dataSource()) {
-    ServiceWorkerNetworkProvider* provider =
-        ServiceWorkerNetworkProvider::FromDocumentState(
-            DocumentState::FromDataSource(frame->dataSource()));
+    blink::WebServiceWorkerNetworkProvider* provider =
+        frame->dataSource()->getServiceWorkerNetworkProvider();
     DCHECK(provider);
-    provider_id = provider->provider_id();
+    provider->willSendRequest(request);
+
     // If the provider does not have a controller at this point, the renderer
     // expects the request to never be handled by a controlling service worker,
     // so set the ServiceWorkerMode to skip local workers here. Otherwise, a
     // service worker that is in the process of becoming the controller (i.e.,
     // via claim()) on the browser-side could handle the request and break
     // the assumptions of the renderer.
-    if (!provider->IsControlledByServiceWorker() &&
+    if (!provider->isControlledByServiceWorker() &&
         request.getServiceWorkerMode() !=
             blink::WebURLRequest::ServiceWorkerMode::None) {
       request.setServiceWorkerMode(
@@ -4315,7 +4311,10 @@
   WebFrame* parent = frame->parent();
   int parent_routing_id = parent ? GetRoutingIdForFrameOrProxy(parent) : -1;
 
-  RequestExtraData* extra_data = new RequestExtraData();
+  RequestExtraData* extra_data =
+      static_cast<RequestExtraData*>(request.getExtraData());
+  if (!extra_data)
+    extra_data = new RequestExtraData();
   extra_data->set_visibility_state(visibilityState());
   extra_data->set_custom_user_agent(custom_user_agent);
   extra_data->set_requested_with(requested_with);
@@ -4329,7 +4328,6 @@
       navigation_state->common_params().allow_download);
   extra_data->set_transition_type(transition_type);
   extra_data->set_should_replace_current_entry(should_replace_current_entry);
-  extra_data->set_service_worker_provider_id(provider_id);
   extra_data->set_stream_override(std::move(stream_override));
   bool is_prefetch =
       GetContentClient()->renderer()->IsPrefetchOnly(this, request);
@@ -4661,22 +4659,6 @@
   return screen_orientation_dispatcher_;
 }
 
-bool RenderFrameImpl::isControlledByServiceWorker(WebDataSource& data_source) {
-  ServiceWorkerNetworkProvider* provider =
-      ServiceWorkerNetworkProvider::FromDocumentState(
-          DocumentState::FromDataSource(&data_source));
-  return provider->IsControlledByServiceWorker();
-}
-
-int64_t RenderFrameImpl::serviceWorkerID(WebDataSource& data_source) {
-  ServiceWorkerNetworkProvider* provider =
-      ServiceWorkerNetworkProvider::FromDocumentState(
-          DocumentState::FromDataSource(&data_source));
-  if (provider->context() && provider->context()->controller())
-    return provider->context()->controller()->version_id();
-  return kInvalidServiceWorkerVersionId;
-}
-
 void RenderFrameImpl::postAccessibilityEvent(const blink::WebAXObject& obj,
                                              blink::WebAXEvent event) {
   HandleWebAccessibilityEvent(obj, event);
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 763807cf..b16656cc 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -649,8 +649,6 @@
   blink::WebString doNotTrackValue() override;
   bool allowWebGL(bool default_value) override;
   blink::WebScreenOrientationClient* webScreenOrientationClient() override;
-  bool isControlledByServiceWorker(blink::WebDataSource& data_source) override;
-  int64_t serviceWorkerID(blink::WebDataSource& data_source) override;
   void postAccessibilityEvent(const blink::WebAXObject& obj,
                               blink::WebAXEvent event) override;
   void handleAccessibilityFindInPageResult(
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 3f3668c3..6faaaa70 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -66,6 +66,7 @@
 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "third_party/WebKit/public/web/WebDataSource.h"
 #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h"
 #include "third_party/WebKit/public/web/WebFrameContentDumper.h"
@@ -1956,34 +1957,37 @@
 }
 
 TEST_F(RenderViewImplTest, ServiceWorkerNetworkProviderSetup) {
-  ServiceWorkerNetworkProvider* provider = NULL;
-  RequestExtraData* extra_data = NULL;
+  blink::WebServiceWorkerNetworkProvider* webprovider = nullptr;
+  ServiceWorkerNetworkProvider* provider = nullptr;
+  RequestExtraData* extra_data = nullptr;
 
   // Make sure each new document has a new provider and
   // that the main request is tagged with the provider's id.
   LoadHTML("<b>A Document</b>");
   ASSERT_TRUE(GetMainFrame()->dataSource());
-  provider = ServiceWorkerNetworkProvider::FromDocumentState(
-      DocumentState::FromDataSource(GetMainFrame()->dataSource()));
-  ASSERT_TRUE(provider);
+  webprovider = GetMainFrame()->dataSource()->getServiceWorkerNetworkProvider();
+  ASSERT_TRUE(webprovider);
   extra_data = static_cast<RequestExtraData*>(
       GetMainFrame()->dataSource()->getRequest().getExtraData());
   ASSERT_TRUE(extra_data);
-  EXPECT_EQ(extra_data->service_worker_provider_id(),
-            provider->provider_id());
+  provider = ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
+      webprovider);
+  ASSERT_TRUE(provider);
+  EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id());
   int provider1_id = provider->provider_id();
 
   LoadHTML("<b>New Document B Goes Here</b>");
   ASSERT_TRUE(GetMainFrame()->dataSource());
-  provider = ServiceWorkerNetworkProvider::FromDocumentState(
-      DocumentState::FromDataSource(GetMainFrame()->dataSource()));
+  webprovider = GetMainFrame()->dataSource()->getServiceWorkerNetworkProvider();
+  ASSERT_TRUE(provider);
+  provider = ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
+      webprovider);
   ASSERT_TRUE(provider);
   EXPECT_NE(provider1_id, provider->provider_id());
   extra_data = static_cast<RequestExtraData*>(
       GetMainFrame()->dataSource()->getRequest().getExtraData());
   ASSERT_TRUE(extra_data);
-  EXPECT_EQ(extra_data->service_worker_provider_id(),
-            provider->provider_id());
+  EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id());
 
   // See that subresource requests are also tagged with the provider's id.
   EXPECT_EQ(frame(), RenderFrameImpl::FromWebFrame(GetMainFrame()));
@@ -1993,8 +1997,7 @@
   frame()->willSendRequest(GetMainFrame(), request);
   extra_data = static_cast<RequestExtraData*>(request.getExtraData());
   ASSERT_TRUE(extra_data);
-  EXPECT_EQ(extra_data->service_worker_provider_id(),
-            provider->provider_id());
+  EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id());
 }
 
 TEST_F(RenderViewImplTest, OnSetAccessibilityMode) {
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index babd858..838a19b4 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -61,12 +61,11 @@
 #include "third_party/WebKit/public/platform/modules/payments/WebPaymentAppRequest.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerClientQueryOptions.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
-#include "third_party/WebKit/public/web/WebDataSource.h"
 #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h"
 #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h"
-#include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 
 namespace content {
 
@@ -84,36 +83,28 @@
       WorkerContextDestroyed(embedded_worker_id);
 }
 
-// We store an instance of this class in the "extra data" of the WebDataSource
-// and attach a ServiceWorkerNetworkProvider to it as base::UserData.
-// (see createServiceWorkerNetworkProvider).
-class DataSourceExtraData
-    : public blink::WebDataSource::ExtraData,
-      public base::SupportsUserData {
- public:
-  DataSourceExtraData() {}
-  ~DataSourceExtraData() override {}
-};
-
 // Called on the main thread only and blink owns it.
 class WebServiceWorkerNetworkProviderImpl
     : public blink::WebServiceWorkerNetworkProvider {
  public:
+  explicit WebServiceWorkerNetworkProviderImpl(
+      std::unique_ptr<ServiceWorkerNetworkProvider> provider)
+      : provider_(std::move(provider)) {}
+
   // Blink calls this method for each request starting with the main script,
   // we tag them with the provider id.
-  void willSendRequest(blink::WebDataSource* data_source,
-                       blink::WebURLRequest& request) override {
-    ServiceWorkerNetworkProvider* provider =
-        ServiceWorkerNetworkProvider::FromDocumentState(
-            static_cast<DataSourceExtraData*>(data_source->getExtraData()));
+  void willSendRequest(blink::WebURLRequest& request) override {
     std::unique_ptr<RequestExtraData> extra_data(new RequestExtraData);
-    extra_data->set_service_worker_provider_id(provider->provider_id());
+    extra_data->set_service_worker_provider_id(provider_->provider_id());
     extra_data->set_originated_from_service_worker(true);
     // Service workers are only available in secure contexts, so all requests
     // are initiated in a secure context.
     extra_data->set_initiated_in_secure_context(true);
     request.setExtraData(extra_data.release());
   }
+
+ private:
+  std::unique_ptr<ServiceWorkerNetworkProvider> provider_;
 };
 
 ServiceWorkerStatusCode EventResultToStatus(
@@ -795,8 +786,7 @@
 }
 
 blink::WebServiceWorkerNetworkProvider*
-ServiceWorkerContextClient::createServiceWorkerNetworkProvider(
-    blink::WebDataSource* data_source) {
+ServiceWorkerContextClient::createServiceWorkerNetworkProvider() {
   DCHECK(main_thread_task_runner_->RunsTasksOnCurrentThread());
 
   // Create a content::ServiceWorkerNetworkProvider for this data source so
@@ -811,15 +801,8 @@
   provider->SetServiceWorkerVersionId(service_worker_version_id_,
                                       embedded_worker_id_);
 
-  // The provider is kept around for the lifetime of the DataSource
-  // and ownership is transferred to the DataSource.
-  DataSourceExtraData* extra_data = new DataSourceExtraData();
-  data_source->setExtraData(extra_data);
-  ServiceWorkerNetworkProvider::AttachToDocumentState(extra_data,
-                                                      std::move(provider));
-
   // Blink is responsible for deleting the returned object.
-  return new WebServiceWorkerNetworkProviderImpl();
+  return new WebServiceWorkerNetworkProviderImpl(std::move(provider));
 }
 
 blink::WebServiceWorkerProvider*
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index ba76461..cc7ae1d 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -40,7 +40,6 @@
 
 namespace blink {
 class WebDataConsumerHandle;
-class WebDataSource;
 struct WebServiceWorkerClientQueryOptions;
 class WebServiceWorkerContextProxy;
 class WebServiceWorkerProvider;
@@ -176,8 +175,8 @@
                                     double dispatch_event_time) override;
 
   // Called on the main thread.
-  blink::WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider(
-      blink::WebDataSource* data_source) override;
+  blink::WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider()
+      override;
   blink::WebServiceWorkerProvider* createServiceWorkerProvider() override;
 
   void postMessageToClient(const blink::WebString& uuid,
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index eb8d6e33..cb5ef55 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -26,10 +26,9 @@
 #include "ipc/ipc_message_macros.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
-#include "third_party/WebKit/public/web/WebDataSource.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "third_party/WebKit/public/web/WebSharedWorker.h"
 #include "third_party/WebKit/public/web/WebSharedWorkerClient.h"
-#include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "url/origin.h"
 
 namespace content {
@@ -66,33 +65,21 @@
   }
 };
 
-// We store an instance of this class in the "extra data" of the WebDataSource
-// and attach a ServiceWorkerNetworkProvider to it as base::UserData.
-// (see createServiceWorkerNetworkProvider).
-class DataSourceExtraData
-    : public blink::WebDataSource::ExtraData,
-      public base::SupportsUserData {
- public:
-  DataSourceExtraData() {}
-  ~DataSourceExtraData() override {}
-  bool is_secure_context = false;
-};
-
 // Called on the main thread only and blink owns it.
 class WebServiceWorkerNetworkProviderImpl
     : public blink::WebServiceWorkerNetworkProvider {
  public:
+  WebServiceWorkerNetworkProviderImpl(
+      std::unique_ptr<ServiceWorkerNetworkProvider> provider,
+      bool is_secure_context)
+      : provider_(std::move(provider)), is_secure_context_(is_secure_context) {}
+
   // Blink calls this method for each request starting with the main script,
   // we tag them with the provider id.
-  void willSendRequest(blink::WebDataSource* data_source,
-                       blink::WebURLRequest& request) override {
-    ServiceWorkerNetworkProvider* provider =
-        GetNetworkProviderFromDataSource(data_source);
+  void willSendRequest(blink::WebURLRequest& request) override {
     std::unique_ptr<RequestExtraData> extra_data(new RequestExtraData);
-    extra_data->set_service_worker_provider_id(provider->provider_id());
-    extra_data->set_initiated_in_secure_context(
-        static_cast<DataSourceExtraData*>(data_source->getExtraData())
-            ->is_secure_context);
+    extra_data->set_service_worker_provider_id(provider_->provider_id());
+    extra_data->set_initiated_in_secure_context(is_secure_context_);
     request.setExtraData(extra_data.release());
     // If the provider does not have a controller at this point, the renderer
     // expects subresource requests to never be handled by a controlling service
@@ -102,7 +89,7 @@
     // request and break the assumptions of the renderer.
     if (request.getRequestContext() !=
             blink::WebURLRequest::RequestContextSharedWorker &&
-        !provider->IsControlledByServiceWorker() &&
+        !provider_->IsControlledByServiceWorker() &&
         request.getServiceWorkerMode() !=
             blink::WebURLRequest::ServiceWorkerMode::None) {
       request.setServiceWorkerMode(
@@ -110,26 +97,19 @@
     }
   }
 
-  bool isControlledByServiceWorker(blink::WebDataSource& data_source) override {
-    ServiceWorkerNetworkProvider* provider =
-        GetNetworkProviderFromDataSource(&data_source);
-    return provider->IsControlledByServiceWorker();
+  bool isControlledByServiceWorker() override {
+    return provider_->IsControlledByServiceWorker();
   }
 
-  int64_t serviceWorkerID(blink::WebDataSource& data_source) override {
-    ServiceWorkerNetworkProvider* provider =
-        GetNetworkProviderFromDataSource(&data_source);
-    if (provider->context()->controller())
-      return provider->context()->controller()->version_id();
+  int64_t serviceWorkerID() override {
+    if (provider_->context()->controller())
+      return provider_->context()->controller()->version_id();
     return kInvalidServiceWorkerVersionId;
   }
 
  private:
-  ServiceWorkerNetworkProvider* GetNetworkProviderFromDataSource(
-      const blink::WebDataSource* data_source) {
-    return ServiceWorkerNetworkProvider::FromDocumentState(
-        static_cast<DataSourceExtraData*>(data_source->getExtraData()));
-  }
+  std::unique_ptr<ServiceWorkerNetworkProvider> provider_;
+  const bool is_secure_context_;
 };
 
 }  // namespace
@@ -245,8 +225,7 @@
 }
 
 blink::WebServiceWorkerNetworkProvider*
-EmbeddedSharedWorkerStub::createServiceWorkerNetworkProvider(
-    blink::WebDataSource* data_source) {
+EmbeddedSharedWorkerStub::createServiceWorkerNetworkProvider() {
   // Create a content::ServiceWorkerNetworkProvider for this data source so
   // we can observe its requests.
   std::unique_ptr<ServiceWorkerNetworkProvider> provider(
@@ -254,16 +233,9 @@
           route_id_, SERVICE_WORKER_PROVIDER_FOR_SHARED_WORKER,
           true /* is_parent_frame_secure */));
 
-  // The provider is kept around for the lifetime of the DataSource
-  // and ownership is transferred to the DataSource.
-  DataSourceExtraData* extra_data = new DataSourceExtraData();
-  extra_data->is_secure_context = IsOriginSecure(url_);
-  data_source->setExtraData(extra_data);
-  ServiceWorkerNetworkProvider::AttachToDocumentState(extra_data,
-                                                      std::move(provider));
-
   // Blink is responsible for deleting the returned object.
-  return new WebServiceWorkerNetworkProviderImpl();
+  return new WebServiceWorkerNetworkProviderImpl(std::move(provider),
+                                                 IsOriginSecure(url_));
 }
 
 void EmbeddedSharedWorkerStub::sendDevToolsMessage(
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.h b/content/renderer/shared_worker/embedded_shared_worker_stub.h
index 20656c8..4985746 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.h
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.h
@@ -73,8 +73,8 @@
   blink::WebWorkerContentSettingsClientProxy*
   createWorkerContentSettingsClientProxy(
       const blink::WebSecurityOrigin& origin) override;
-  blink::WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider(
-      blink::WebDataSource*) override;
+  blink::WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider()
+      override;
   void sendDevToolsMessage(int session_id,
                            int call_id,
                            const blink::WebString& message,
diff --git a/ios/chrome/browser/ui/bookmarks/BUILD.gn b/ios/chrome/browser/ui/bookmarks/BUILD.gn
index 5887084..bbe8c4e 100644
--- a/ios/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/BUILD.gn
@@ -2,65 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("bookmarks") {
+source_set("bookmarks_arc") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "bookmark_collection_cells.h",
-    "bookmark_collection_cells.mm",
-    "bookmark_collection_view.h",
-    "bookmark_collection_view.mm",
-    "bookmark_collection_view_background.h",
-    "bookmark_collection_view_background.mm",
-    "bookmark_controller_factory.h",
-    "bookmark_controller_factory.mm",
-    "bookmark_edit_view_controller.h",
-    "bookmark_edit_view_controller.mm",
-    "bookmark_elevated_toolbar.h",
-    "bookmark_elevated_toolbar.mm",
-    "bookmark_extended_button.h",
-    "bookmark_extended_button.mm",
-    "bookmark_folder_collection_view.h",
-    "bookmark_folder_collection_view.mm",
-    "bookmark_folder_editor_view_controller.h",
-    "bookmark_folder_editor_view_controller.mm",
-    "bookmark_folder_table_view_cell.h",
-    "bookmark_folder_table_view_cell.mm",
-    "bookmark_folder_view_controller.h",
-    "bookmark_folder_view_controller.mm",
     "bookmark_home_handset_view_controller.h",
     "bookmark_home_handset_view_controller.mm",
-    "bookmark_home_primary_view.h",
     "bookmark_home_tablet_ntp_controller.h",
     "bookmark_home_tablet_ntp_controller.mm",
-    "bookmark_home_view_controller.h",
-    "bookmark_home_view_controller.mm",
-    "bookmark_home_waiting_view.h",
-    "bookmark_home_waiting_view.mm",
-    "bookmark_interaction_controller.h",
-    "bookmark_interaction_controller.mm",
-    "bookmark_menu_cell.h",
-    "bookmark_menu_cell.mm",
-    "bookmark_menu_item.h",
-    "bookmark_menu_item.mm",
-    "bookmark_menu_view.h",
-    "bookmark_menu_view.mm",
-    "bookmark_model_bridge_observer.h",
-    "bookmark_model_bridge_observer.mm",
-    "bookmark_navigation_controller.h",
-    "bookmark_navigation_controller.mm",
-    "bookmark_panel_view.h",
-    "bookmark_panel_view.mm",
-    "bookmark_position_cache.h",
-    "bookmark_position_cache.mm",
-    "bookmark_promo_cell.h",
-    "bookmark_promo_cell.mm",
-    "bookmark_promo_controller.h",
-    "bookmark_promo_controller.mm",
     "bookmark_utils_ios.h",
     "bookmark_utils_ios.mm",
-    "undo_manager_bridge_observer.h",
-    "undo_manager_bridge_observer.mm",
-    "undo_manager_wrapper.h",
-    "undo_manager_wrapper.mm",
   ]
   deps = [
     ":resources",
@@ -121,13 +71,151 @@
   ]
 }
 
+source_set("bookmarks") {
+  sources = [
+    "bookmark_collection_cells.h",
+    "bookmark_collection_cells.mm",
+    "bookmark_collection_view.h",
+    "bookmark_collection_view.mm",
+    "bookmark_collection_view_background.h",
+    "bookmark_collection_view_background.mm",
+    "bookmark_controller_factory.h",
+    "bookmark_controller_factory.mm",
+    "bookmark_edit_view_controller.h",
+    "bookmark_edit_view_controller.mm",
+    "bookmark_elevated_toolbar.h",
+    "bookmark_elevated_toolbar.mm",
+    "bookmark_extended_button.h",
+    "bookmark_extended_button.mm",
+    "bookmark_folder_collection_view.h",
+    "bookmark_folder_collection_view.mm",
+    "bookmark_folder_editor_view_controller.h",
+    "bookmark_folder_editor_view_controller.mm",
+    "bookmark_folder_table_view_cell.h",
+    "bookmark_folder_table_view_cell.mm",
+    "bookmark_folder_view_controller.h",
+    "bookmark_folder_view_controller.mm",
+    "bookmark_home_primary_view.h",
+    "bookmark_home_view_controller.h",
+    "bookmark_home_view_controller.mm",
+    "bookmark_home_waiting_view.h",
+    "bookmark_home_waiting_view.mm",
+    "bookmark_interaction_controller.h",
+    "bookmark_interaction_controller.mm",
+    "bookmark_menu_cell.h",
+    "bookmark_menu_cell.mm",
+    "bookmark_menu_item.h",
+    "bookmark_menu_item.mm",
+    "bookmark_menu_view.h",
+    "bookmark_menu_view.mm",
+    "bookmark_model_bridge_observer.h",
+    "bookmark_model_bridge_observer.mm",
+    "bookmark_navigation_controller.h",
+    "bookmark_navigation_controller.mm",
+    "bookmark_panel_view.h",
+    "bookmark_panel_view.mm",
+    "bookmark_position_cache.h",
+    "bookmark_position_cache.mm",
+    "bookmark_promo_cell.h",
+    "bookmark_promo_cell.mm",
+    "bookmark_promo_controller.h",
+    "bookmark_promo_controller.mm",
+    "undo_manager_bridge_observer.h",
+    "undo_manager_bridge_observer.mm",
+    "undo_manager_wrapper.h",
+    "undo_manager_wrapper.mm",
+  ]
+  deps = [
+    ":resources",
+    "//base",
+    "//base:i18n",
+    "//components/bookmarks/browser",
+    "//components/favicon/core",
+    "//components/favicon_base",
+    "//components/pref_registry",
+    "//components/prefs",
+    "//components/query_parser",
+    "//components/signin/core/browser",
+    "//components/strings",
+    "//components/undo",
+    "//components/url_formatter",
+    "//google_apis",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/app/theme",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/bookmarks",
+    "//ios/chrome/browser/bookmarks:bookmarks_utils",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/favicon",
+    "//ios/chrome/browser/metrics:metrics_internal",
+    "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/chrome/browser/ui/bookmarks/bars",
+    "//ios/chrome/browser/ui/bookmarks/cells",
+    "//ios/chrome/browser/ui/collection_view",
+    "//ios/chrome/browser/ui/collection_view/cells",
+    "//ios/chrome/browser/ui/colors",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/icons",
+    "//ios/chrome/browser/ui/keyboard",
+    "//ios/chrome/browser/ui/material_components",
+    "//ios/chrome/browser/ui/ntp",
+    "//ios/chrome/browser/undo",
+    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/ui",
+    "//ios/third_party/material_components_ios",
+    "//ios/third_party/material_roboto_font_loader_ios",
+    "//ios/web",
+    "//skia",
+    "//ui/base",
+    "//ui/gfx",
+    "//url",
+  ]
+  public_deps = [
+    ":bookmarks_arc",
+  ]
+  allow_circular_includes_from = [
+    "//ios/chrome/browser/ui/bookmarks/bars",
+    "//ios/chrome/browser/ui/bookmarks/cells",
+    ":bookmarks_arc",
+  ]
+  libs = [
+    "CoreGraphics.framework",
+    "QuartzCore.framework",
+    "UIKit.framework",
+  ]
+}
+
+source_set("unit_tests_arc") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "bookmark_utils_ios_unittest.mm",
+  ]
+  deps = [
+    ":bookmarks",
+    "//base",
+    "//components/bookmarks/browser",
+    "//components/bookmarks/test",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/bookmarks",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/web:test_support",
+    "//testing/gtest",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [
     "bookmark_home_view_controller_unittest.mm",
     "bookmark_ios_unittest.mm",
     "bookmark_position_cache_unittest.mm",
-    "bookmark_utils_ios_unittest.mm",
+  ]
+  public_deps = [
+    ":unit_tests_arc",
   ]
   deps = [
     ":bookmarks",
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.mm
index 0c88d0d7..cc4e76b 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.mm
@@ -6,10 +6,7 @@
 
 #include <memory>
 
-#import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
-#include "base/mac/objc_property_releaser.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
@@ -42,6 +39,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using bookmarks::BookmarkNode;
 
 namespace {
@@ -59,56 +60,54 @@
     BookmarkPromoControllerDelegate> {
   // Bridge to register for bookmark changes.
   std::unique_ptr<bookmarks::BookmarkModelBridge> _bridge;
-  base::mac::ObjCPropertyReleaser
-      _propertyReleaser_BookmarkHomeHandsetViewController;
 }
 
 // This views holds the primary content of this view controller. At any point in
 // time, it contains exactly one of the BookmarkCollectionView subclasses.
-@property(nonatomic, retain) UIView* contentView;
+@property(nonatomic, strong) UIView* contentView;
 // The possible views that can be shown from the menu.
-@property(nonatomic, retain) BookmarkFolderCollectionView* folderView;
+@property(nonatomic, strong) BookmarkFolderCollectionView* folderView;
 // This view is created and used if the model is not fully loaded yet by the
 // time this controller starts.
-@property(nonatomic, retain) BookmarkHomeWaitingView* waitForModelView;
+@property(nonatomic, strong) BookmarkHomeWaitingView* waitForModelView;
 
 // The menu with all the folders and special entries.
-@property(nonatomic, retain) BookmarkMenuView* menuView;
+@property(nonatomic, strong) BookmarkMenuView* menuView;
 // At any point in time, there is exactly one collection view whose view is part
 // of the view hierarchy. This property determine which collection view is
 // visible. Not by accident, this property also reflects the selected menu item
 // in the BookmarkMenuView.
-@property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem;
+@property(nonatomic, strong) BookmarkMenuItem* primaryMenuItem;
 // When the view is first shown on the screen, this property represents the
 // cached value of the y of the content offset of the primary view. This
 // property is set to nil after it is used.
-@property(nonatomic, retain) NSNumber* cachedContentPosition;
+@property(nonatomic, strong) NSNumber* cachedContentPosition;
 
 // The navigation bar sits on top of the main content.
-@property(nonatomic, retain) BookmarkNavigationBar* navigationBar;
+@property(nonatomic, strong) BookmarkNavigationBar* navigationBar;
 // The layout code in this class relies on the assumption that the editingBar
 // has the same frame as the navigationBar.
-@property(nonatomic, retain) BookmarkEditingBar* editingBar;
+@property(nonatomic, strong) BookmarkEditingBar* editingBar;
 
 // The action sheet coordinator used when trying to edit a single bookmark.
-@property(nonatomic, retain) ActionSheetCoordinator* actionSheetCoordinator;
+@property(nonatomic, strong) ActionSheetCoordinator* actionSheetCoordinator;
 // The view controller used to view and edit a single bookmark.
-@property(nonatomic, retain) BookmarkEditViewController* editViewController;
+@property(nonatomic, strong) BookmarkEditViewController* editViewController;
 // The view controller used to pick a folder in which to move the selected
 // bookmarks.
-@property(nonatomic, retain) BookmarkFolderViewController* folderSelector;
+@property(nonatomic, strong) BookmarkFolderViewController* folderSelector;
 // The view controller to present when editing the current folder.
-@property(nonatomic, retain) BookmarkFolderEditorViewController* folderEditor;
+@property(nonatomic, strong) BookmarkFolderEditorViewController* folderEditor;
 #pragma mark Specific to this class.
 
 // The panel view slides on top of the content to display the menu.
-@property(nonatomic, retain) BookmarkPanelView* panelView;
+@property(nonatomic, strong) BookmarkPanelView* panelView;
 
 // Either the menu or the primaryView can scrollToTop.
 @property(nonatomic, assign) BOOL scrollingMenuToTop;
 // The controller managing the display of the promo cell and the promo view
 // controller.
-@property(nonatomic, retain) BookmarkPromoController* bookmarkPromoController;
+@property(nonatomic, strong) BookmarkPromoController* bookmarkPromoController;
 
 #pragma mark View loading and switching
 // This method is called if the view needs to be loaded and the model is not
@@ -239,9 +238,6 @@
                   browserState:(ios::ChromeBrowserState*)browserState {
   self = [super initWithLoader:loader browserState:browserState];
   if (self) {
-    _propertyReleaser_BookmarkHomeHandsetViewController.Init(
-        self, [BookmarkHomeHandsetViewController class]);
-
     _bridge.reset(new bookmarks::BookmarkModelBridge(self, self.bookmarks));
     // It is important to initialize the promo controller with the browser state
     // passed in, as it could be incognito.
@@ -261,7 +257,6 @@
   _folderSelector.delegate = nil;
 
   _panelView.delegate = nil;
-  [super dealloc];
 }
 
 - (void)removeEditNode:(const BookmarkNode*)node
@@ -278,8 +273,8 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  base::scoped_nsobject<BookmarkNavigationBar> bar(
-      [[BookmarkNavigationBar alloc] initWithFrame:[self navigationBarFrame]]);
+  BookmarkNavigationBar* bar =
+      [[BookmarkNavigationBar alloc] initWithFrame:[self navigationBarFrame]];
   self.navigationBar = bar;
   [self.navigationBar setEditTarget:self
                              action:@selector(navigationBarWantsEditing:)];
@@ -316,8 +311,8 @@
   DCHECK([self isViewLoaded]);
 
   // Present a waiting view.
-  base::scoped_nsobject<BookmarkHomeWaitingView> waitingView(
-      [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds]);
+  BookmarkHomeWaitingView* waitingView =
+      [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds];
   self.waitForModelView = waitingView;
   [self.view addSubview:self.waitForModelView];
   [self.waitForModelView startWaiting];
@@ -327,15 +322,15 @@
   DCHECK(self.bookmarks->loaded());
   DCHECK([self isViewLoaded]);
 
-  self.panelView = base::scoped_nsobject<BookmarkPanelView>(
+  self.panelView =
       [[BookmarkPanelView alloc] initWithFrame:[self frameForPrimaryView]
-                                 menuViewWidth:kBookmarkMenuWidth]);
+                                 menuViewWidth:kBookmarkMenuWidth];
   self.panelView.autoresizingMask =
       UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
   self.panelView.delegate = self;
   [self.view insertSubview:self.panelView atIndex:0];
 
-  self.contentView = base::scoped_nsobject<UIView>([[UIView alloc] init]);
+  self.contentView = [[UIView alloc] init];
   self.contentView.frame = self.panelView.contentView.bounds;
   self.contentView.autoresizingMask =
       UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
@@ -343,10 +338,9 @@
 
   // The user can swipe the BookmarkPanelView to show the menuView.
   // Therefore, it must be created here.
-  self.menuView =
-      base::scoped_nsobject<BookmarkMenuView>([[BookmarkMenuView alloc]
-          initWithBrowserState:self.browserState
-                         frame:self.panelView.menuView.bounds]);
+  self.menuView = [[BookmarkMenuView alloc]
+      initWithBrowserState:self.browserState
+                     frame:self.panelView.menuView.bounds];
   self.menuView.delegate = self;
   [self.panelView.menuView addSubview:self.menuView];
   self.menuView.autoresizingMask =
@@ -379,10 +373,9 @@
   if (self.folderView)
     return;
 
-  base::scoped_nsobject<BookmarkFolderCollectionView> view(
-      [[BookmarkFolderCollectionView alloc]
-          initWithBrowserState:self.browserState
-                         frame:[self frameForPrimaryView]]);
+  BookmarkFolderCollectionView* view = [[BookmarkFolderCollectionView alloc]
+      initWithBrowserState:self.browserState
+                     frame:[self frameForPrimaryView]];
   self.folderView = view;
   self.folderView.delegate = self;
   [self.folderView setEditing:self.editing animated:NO];
@@ -438,8 +431,8 @@
   }
 
   if (!self.editingBar) {
-    self.editingBar = base::scoped_nsobject<BookmarkEditingBar>(
-        [[BookmarkEditingBar alloc] initWithFrame:self.navigationBar.frame]);
+    self.editingBar =
+        [[BookmarkEditingBar alloc] initWithFrame:self.navigationBar.frame];
     [self.editingBar setCancelTarget:self action:@selector(editingBarCancel)];
     [self.editingBar setDeleteTarget:self action:@selector(editingBarDelete)];
     [self.editingBar setMoveTarget:self action:@selector(editingBarMove)];
@@ -602,20 +595,20 @@
                        forCell:(BookmarkItemCell*)cell {
   DCHECK(!self.editViewController);
   DCHECK(!self.actionSheetCoordinator);
-  self.actionSheetCoordinator = [[[ActionSheetCoordinator alloc]
-      initWithBaseViewController:self
-                           title:nil
-                         message:nil
-                            rect:CGRectZero
-                            view:nil] autorelease];
-  base::WeakNSObject<BookmarkHomeHandsetViewController> weakSelf(self);
+  self.actionSheetCoordinator =
+      [[ActionSheetCoordinator alloc] initWithBaseViewController:self
+                                                           title:nil
+                                                         message:nil
+                                                            rect:CGRectZero
+                                                            view:nil];
+  __weak BookmarkHomeHandsetViewController* weakSelf = self;
 
   // Select action.
   [self.actionSheetCoordinator
       addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_SELECT)
                 action:^{
                   [weakSelf selectFirstNode:node withCell:cell];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -624,7 +617,7 @@
       addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_EDIT)
                 action:^{
                   [weakSelf editNode:node];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -635,7 +628,7 @@
                   std::set<const BookmarkNode*> nodes;
                   nodes.insert(node);
                   [weakSelf moveNodes:nodes];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -646,7 +639,7 @@
                   std::set<const BookmarkNode*> nodes;
                   nodes.insert(node);
                   [weakSelf deleteNodes:nodes];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDestructive];
 
@@ -654,7 +647,7 @@
   [self.actionSheetCoordinator
       addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
                 action:^{
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleCancel];
 
@@ -703,17 +696,16 @@
     self.folderEditor = folderEditor;
     editorController = folderEditor;
   } else {
-    base::scoped_nsobject<BookmarkEditViewController> controller([
-        [BookmarkEditViewController alloc] initWithBookmark:node
-                                               browserState:self.browserState]);
+    BookmarkEditViewController* controller =
+        [[BookmarkEditViewController alloc] initWithBookmark:node
+                                                browserState:self.browserState];
     self.editViewController = controller;
     self.editViewController.delegate = self;
     editorController = self.editViewController;
   }
   DCHECK(editorController);
-  base::scoped_nsobject<UINavigationController> navController(
-      [[BookmarkNavigationController alloc]
-          initWithRootViewController:editorController]);
+  UINavigationController* navController = [[BookmarkNavigationController alloc]
+      initWithRootViewController:editorController];
   [navController setModalPresentationStyle:UIModalPresentationFormSheet];
   [self presentViewController:navController animated:YES completion:NULL];
 }
@@ -723,17 +715,15 @@
   DCHECK(nodes.size() > 0);
   const BookmarkNode* editedNode = *(nodes.begin());
   const BookmarkNode* selectedFolder = editedNode->parent();
-  self.folderSelector = base::scoped_nsobject<BookmarkFolderViewController>(
-      [[BookmarkFolderViewController alloc]
-          initWithBookmarkModel:self.bookmarks
-               allowsNewFolders:YES
-                    editedNodes:nodes
-                   allowsCancel:YES
-                 selectedFolder:selectedFolder]);
+  self.folderSelector = [[BookmarkFolderViewController alloc]
+      initWithBookmarkModel:self.bookmarks
+           allowsNewFolders:YES
+                editedNodes:nodes
+               allowsCancel:YES
+             selectedFolder:selectedFolder];
   self.folderSelector.delegate = self;
-  base::scoped_nsobject<UINavigationController> navController(
-      [[BookmarkNavigationController alloc]
-          initWithRootViewController:self.folderSelector]);
+  UINavigationController* navController = [[BookmarkNavigationController alloc]
+      initWithRootViewController:self.folderSelector];
   [navController setModalPresentationStyle:UIModalPresentationFormSheet];
   [self presentViewController:navController animated:YES completion:NULL];
 }
@@ -898,9 +888,9 @@
   folderEditor.delegate = self;
   self.folderEditor = folderEditor;
 
-  base::scoped_nsobject<BookmarkNavigationController> navController(
+  BookmarkNavigationController* navController =
       [[BookmarkNavigationController alloc]
-          initWithRootViewController:self.folderEditor]);
+          initWithRootViewController:self.folderEditor];
   [navController setModalPresentationStyle:UIModalPresentationFormSheet];
   [self presentViewController:navController animated:YES completion:NULL];
 }
@@ -1016,20 +1006,19 @@
     return;
 
   DCHECK(self.waitForModelView);
-  base::WeakNSObject<BookmarkHomeHandsetViewController> weakSelf(self);
+  __weak BookmarkHomeHandsetViewController* weakSelf = self;
   [self.waitForModelView stopWaitingWithCompletion:^{
-    base::scoped_nsobject<BookmarkHomeHandsetViewController> strongSelf(
-        [weakSelf retain]);
+    BookmarkHomeHandsetViewController* strongSelf = weakSelf;
     // Early return if the controller has been deallocated.
     if (!strongSelf)
       return;
     [UIView animateWithDuration:0.2
         animations:^{
-          strongSelf.get().waitForModelView.alpha = 0.0;
+          strongSelf.waitForModelView.alpha = 0.0;
         }
         completion:^(BOOL finished) {
-          [strongSelf.get().waitForModelView removeFromSuperview];
-          strongSelf.get().waitForModelView = nil;
+          [strongSelf.waitForModelView removeFromSuperview];
+          strongSelf.waitForModelView = nil;
         }];
     [strongSelf loadBookmarkViews];
   }];
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
index d2775972a..d4f8616 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
@@ -7,10 +7,7 @@
 #include <memory>
 
 #include "base/ios/block_types.h"
-#import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
-#include "base/mac/objc_property_releaser.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
@@ -46,6 +43,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/page_transition_types.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using bookmarks::BookmarkNode;
 
 namespace {
@@ -61,7 +62,7 @@
 @end
 
 @interface ContentView : UIView
-@property(nonatomic, assign) id<ContentViewDelegate> delegate;
+@property(nonatomic, weak) id<ContentViewDelegate> delegate;
 @end
 
 @implementation ContentView
@@ -85,7 +86,7 @@
   // Bridge to register for bookmark changes.
   std::unique_ptr<bookmarks::BookmarkModelBridge> _bridge;
   ios::ChromeBrowserState* _browserState;  // Weak.
-  id<UrlLoader> _loader;                   // Weak.
+  __weak id<UrlLoader> _loader;
 
   // The following 2 ivars both represent the set of nodes being edited.
   // The set is for fast lookup.
@@ -94,19 +95,16 @@
   // DO NOT modify these two ivars directly.
   std::set<const BookmarkNode*> _editNodes;
   std::vector<const BookmarkNode*> _editNodesOrdered;
-
-  base::mac::ObjCPropertyReleaser
-      _propertyReleaser_BookmarkHomeTabletNTPController;
 }
 
-@property(nonatomic, retain) BookmarkPanelView* panelView;
+@property(nonatomic, strong) BookmarkPanelView* panelView;
 
 #pragma mark - Properties and methods akin to BookmarkHomeViewController
 
 // Whether the view controller is in editing mode.
 @property(nonatomic, assign) BOOL editing;
 // The set of edited index paths.
-@property(nonatomic, retain) NSMutableArray* editIndexPaths;
+@property(nonatomic, strong) NSMutableArray* editIndexPaths;
 // The bookmark model used.
 @property(nonatomic, assign, readonly) bookmarks::BookmarkModel* bookmarks;
 // The user's browser state model used.
@@ -128,44 +126,44 @@
 
 // This views holds the primary content of this controller. At any point in
 // time, it contains exactly one of the BookmarkCollectionView subclasses.
-@property(nonatomic, retain) ContentView* contentView;
+@property(nonatomic, strong) ContentView* contentView;
 // The possible views that can be shown from the menu.
-@property(nonatomic, retain) BookmarkFolderCollectionView* folderView;
+@property(nonatomic, strong) BookmarkFolderCollectionView* folderView;
 // This view is created and used if the model is not fully loaded yet by the
 // time this controller starts.
-@property(nonatomic, retain) BookmarkHomeWaitingView* waitForModelView;
+@property(nonatomic, strong) BookmarkHomeWaitingView* waitForModelView;
 
 // The menu with all the folders and special entries.
-@property(nonatomic, retain) BookmarkMenuView* menuView;
+@property(nonatomic, strong) BookmarkMenuView* menuView;
 // At any point in time, there is exactly one collection view whose view is part
 // of the view hierarchy. This property determines which collection view is
 // visible. Not by accident, this property also reflects the selected menu item
 // in the BookmarkMenuView.
-@property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem;
+@property(nonatomic, strong) BookmarkMenuItem* primaryMenuItem;
 // When the view is first shown on the screen, this property represents the
 // cached value of the y of the content offset of the primary view. This
 // property is set to nil after it is used.
-@property(nonatomic, retain)
+@property(nonatomic, strong)
     NSNumber* cachedContentPosition;  // FIXME: INACTIVE
 
 // The navigation bar sits on top of the main content.
-@property(nonatomic, retain) BookmarkNavigationBar* navigationBar;
+@property(nonatomic, strong) BookmarkNavigationBar* navigationBar;
 // The editing bar present when items are selected.
-@property(nonatomic, retain) BookmarkEditingBar* editingBar;
+@property(nonatomic, strong) BookmarkEditingBar* editingBar;
 
 // The action sheet coordinator used when trying to edit a single bookmark.
-@property(nonatomic, retain) ActionSheetCoordinator* actionSheetCoordinator;
+@property(nonatomic, strong) ActionSheetCoordinator* actionSheetCoordinator;
 // The view controller used to view and edit a single bookmark.
-@property(nonatomic, retain) BookmarkEditViewController* editViewController;
+@property(nonatomic, strong) BookmarkEditViewController* editViewController;
 // The view controller used to pick a folder in which to move the selected
 // bookmarks.
-@property(nonatomic, retain) BookmarkFolderViewController* folderSelector;
+@property(nonatomic, strong) BookmarkFolderViewController* folderSelector;
 // The view controller to present when editing the current folder.
-@property(nonatomic, retain)
+@property(nonatomic, strong)
     BookmarkFolderEditorViewController* folderEditor;  // FIX
 // The controller managing the display of the promo cell and the promo view
 // controller.
-@property(nonatomic, retain) BookmarkPromoController* bookmarkPromoController;
+@property(nonatomic, strong) BookmarkPromoController* bookmarkPromoController;
 
 #pragma mark Specific to this class.
 
@@ -307,8 +305,6 @@
     DCHECK(browserState);
     _browserState = browserState->GetOriginalChromeBrowserState();
     _loader = loader;
-    _propertyReleaser_BookmarkHomeTabletNTPController.Init(
-        self, [BookmarkHomeTabletNTPController class]);
 
     _bookmarks = ios::BookmarkModelFactory::GetForBrowserState(_browserState);
     _bridge.reset(new bookmarks::BookmarkModelBridge(self, _bookmarks));
@@ -331,8 +327,6 @@
 
   _editViewController.delegate = nil;
   _folderSelector.delegate = nil;
-
-  [super dealloc];
 }
 
 - (ios::ChromeBrowserState*)browserState {
@@ -446,8 +440,8 @@
   DCHECK(self.contentView);
 
   // Present a waiting view.
-  base::scoped_nsobject<BookmarkHomeWaitingView> waitingView(
-      [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds]);
+  BookmarkHomeWaitingView* waitingView =
+      [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds];
   self.waitForModelView = waitingView;
   [self.view addSubview:self.waitForModelView];
   [self.waitForModelView startWaiting];
@@ -467,10 +461,9 @@
   LayoutRect menuLayout =
       LayoutRectMake(0, self.view.bounds.size.width, 0, self.menuWidth,
                      self.view.bounds.size.height);
-  self.menuView =
-      base::scoped_nsobject<BookmarkMenuView>([[BookmarkMenuView alloc]
-          initWithBrowserState:self.browserState
-                         frame:LayoutRectGetRect(menuLayout)]);
+  self.menuView = [[BookmarkMenuView alloc]
+      initWithBrowserState:self.browserState
+                     frame:LayoutRectGetRect(menuLayout)];
   self.menuView.delegate = self;
   self.menuView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
 
@@ -505,10 +498,9 @@
   if (self.folderView)
     return;
 
-  base::scoped_nsobject<BookmarkFolderCollectionView> view(
-      [[BookmarkFolderCollectionView alloc]
-          initWithBrowserState:self.browserState
-                         frame:CGRectZero]);
+  BookmarkFolderCollectionView* view = [[BookmarkFolderCollectionView alloc]
+      initWithBrowserState:self.browserState
+                     frame:CGRectZero];
   self.folderView = view;
   self.folderView.delegate = self;
   [self.folderView setEditing:self.editing animated:NO];
@@ -581,9 +573,9 @@
   if ([self shouldPresentMenuInSlideInPanel]) {
     // Create (if needed), and add the panelView to the view hierarchy.
     if (!self.panelView) {
-      self.panelView = base::scoped_nsobject<BookmarkPanelView>(
+      self.panelView =
           [[BookmarkPanelView alloc] initWithFrame:CGRectZero
-                                     menuViewWidth:[self menuWidth]]);
+                                     menuViewWidth:[self menuWidth]];
     }
     [self.view addSubview:self.panelView];
     CGSize size = self.contentView.bounds.size;
@@ -636,8 +628,8 @@
   }
 
   if (!self.editingBar) {
-    self.editingBar = base::scoped_nsobject<BookmarkEditingBar>(
-        [[BookmarkEditingBar alloc] initWithFrame:[self editingBarFrame]]);
+    self.editingBar =
+        [[BookmarkEditingBar alloc] initWithFrame:[self editingBarFrame]];
     [self.editingBar setCancelTarget:self action:@selector(editingBarCancel)];
     [self.editingBar setDeleteTarget:self action:@selector(editingBarDelete)];
     [self.editingBar setMoveTarget:self action:@selector(editingBarMove)];
@@ -821,20 +813,20 @@
                        forCell:(BookmarkItemCell*)cell {
   DCHECK(!self.editViewController);
   DCHECK(!self.actionSheetCoordinator);
-  self.actionSheetCoordinator = [[[ActionSheetCoordinator alloc]
+  self.actionSheetCoordinator = [[ActionSheetCoordinator alloc]
       initWithBaseViewController:self.view.window.rootViewController
                            title:nil
                          message:nil
                             rect:view.bounds
-                            view:view] autorelease];
-  base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
+                            view:view];
+  __weak BookmarkHomeTabletNTPController* weakSelf = self;
 
   // Select action.
   [self.actionSheetCoordinator
       addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_SELECT)
                 action:^{
                   [weakSelf selectFirstNode:node withCell:cell];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -846,7 +838,7 @@
                   if ([cell isKindOfClass:[BookmarkItemCell class]])
                     itemCell = static_cast<BookmarkItemCell*>(cell);
                   [weakSelf editNode:node withCell:itemCell];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -857,7 +849,7 @@
                   std::set<const BookmarkNode*> nodes;
                   nodes.insert(node);
                   [weakSelf moveNodes:nodes];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDefault];
 
@@ -868,7 +860,7 @@
                   std::set<const BookmarkNode*> nodes;
                   nodes.insert(node);
                   [weakSelf deleteNodes:nodes];
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleDestructive];
 
@@ -876,7 +868,7 @@
   [self.actionSheetCoordinator
       addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
                 action:^{
-                  weakSelf.get().actionSheetCoordinator = nil;
+                  weakSelf.actionSheetCoordinator = nil;
                 }
                  style:UIAlertActionStyleCancel];
 
@@ -926,18 +918,17 @@
     self.folderEditor = folderEditor;
     editorController = folderEditor;
   } else {
-    base::scoped_nsobject<BookmarkEditViewController> controller([
-        [BookmarkEditViewController alloc] initWithBookmark:node
-                                               browserState:self.browserState]);
+    BookmarkEditViewController* controller =
+        [[BookmarkEditViewController alloc] initWithBookmark:node
+                                                browserState:self.browserState];
     self.editViewController = controller;
     self.editViewController.delegate = self;
     editorController = self.editViewController;
   }
   DCHECK(editorController);
-  base::scoped_nsobject<UINavigationController> navController(
-      [[BookmarkNavigationController alloc]
-          initWithRootViewController:editorController]);
-  navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
+  UINavigationController* navController = [[BookmarkNavigationController alloc]
+      initWithRootViewController:editorController];
+  navController.modalPresentationStyle = UIModalPresentationFormSheet;
   [self.view.window.rootViewController presentViewController:navController
                                                     animated:YES
                                                   completion:NULL];
@@ -948,18 +939,16 @@
   DCHECK(nodes.size() > 0);
   const BookmarkNode* editedNode = *(nodes.begin());
   const BookmarkNode* selectedFolder = editedNode->parent();
-  self.folderSelector = base::scoped_nsobject<BookmarkFolderViewController>(
-      [[BookmarkFolderViewController alloc]
-          initWithBookmarkModel:self.bookmarks
-               allowsNewFolders:YES
-                    editedNodes:nodes
-                   allowsCancel:YES
-                 selectedFolder:selectedFolder]);
+  self.folderSelector = [[BookmarkFolderViewController alloc]
+      initWithBookmarkModel:self.bookmarks
+           allowsNewFolders:YES
+                editedNodes:nodes
+               allowsCancel:YES
+             selectedFolder:selectedFolder];
   self.folderSelector.delegate = self;
-  base::scoped_nsobject<UINavigationController> controller(
-      [[BookmarkNavigationController alloc]
-          initWithRootViewController:self.folderSelector]);
-  controller.get().modalPresentationStyle = UIModalPresentationFormSheet;
+  UINavigationController* controller = [[BookmarkNavigationController alloc]
+      initWithRootViewController:self.folderSelector];
+  controller.modalPresentationStyle = UIModalPresentationFormSheet;
   [self.view.window.rootViewController presentViewController:controller
                                                     animated:YES
                                                   completion:NULL];
@@ -1140,10 +1129,10 @@
   folderEditor.delegate = self;
   self.folderEditor = folderEditor;
 
-  base::scoped_nsobject<BookmarkNavigationController> navController(
+  BookmarkNavigationController* navController =
       [[BookmarkNavigationController alloc]
-          initWithRootViewController:self.folderEditor]);
-  navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
+          initWithRootViewController:self.folderEditor];
+  navController.modalPresentationStyle = UIModalPresentationFormSheet;
   [self.view.window.rootViewController presentViewController:navController
                                                     animated:YES
                                                   completion:NULL];
@@ -1198,8 +1187,8 @@
     _contentView.delegate = self;
     self.contentView.backgroundColor =
         bookmark_utils_ios::mainBackgroundColor();
-    base::scoped_nsobject<BookmarkNavigationBar> bar(
-        [[BookmarkNavigationBar alloc] initWithFrame:CGRectZero]);
+    BookmarkNavigationBar* bar =
+        [[BookmarkNavigationBar alloc] initWithFrame:CGRectZero];
     self.navigationBar = bar;
     self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
 
@@ -1232,20 +1221,19 @@
     return;
 
   DCHECK(self.waitForModelView);
-  base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
+  __weak BookmarkHomeTabletNTPController* weakSelf = self;
   [self.waitForModelView stopWaitingWithCompletion:^{
-    base::scoped_nsobject<BookmarkHomeTabletNTPController> strongSelf(
-        [weakSelf retain]);
+    BookmarkHomeTabletNTPController* strongSelf = weakSelf;
     // Early return if the controller has been deallocated.
     if (!strongSelf)
       return;
     [UIView animateWithDuration:0.2
         animations:^{
-          strongSelf.get().waitForModelView.alpha = 0.0;
+          strongSelf.waitForModelView.alpha = 0.0;
         }
         completion:^(BOOL finished) {
-          [strongSelf.get().waitForModelView removeFromSuperview];
-          strongSelf.get().waitForModelView = nil;
+          [strongSelf.waitForModelView removeFromSuperview];
+          strongSelf.waitForModelView = nil;
         }];
     [strongSelf loadBookmarkViews];
   }];
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h
index 0ebdfc8..24880a1 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h
@@ -185,7 +185,7 @@
 // |model| must be loaded.
 // |item| and |position| are out variables, only populated if the return is YES.
 BOOL GetPositionCache(bookmarks::BookmarkModel* model,
-                      BookmarkMenuItem** item,
+                      BookmarkMenuItem* __autoreleasing* item,
                       CGFloat* position);
 // Method exists for testing.
 void ClearPositionCache();
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm
index 55f9d34..d3ecf12 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm
@@ -12,8 +12,6 @@
 #include "base/hash.h"
 #include "base/i18n/string_compare.h"
 #include "base/mac/bind_objc_block.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
@@ -36,6 +34,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/models/tree_node_iterator.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using bookmarks::BookmarkNode;
 
 namespace bookmark_utils_ios {
@@ -187,8 +189,7 @@
 
 UIView* dropShadowWithWidth(CGFloat width) {
   UIImage* shadowImage = [UIImage imageNamed:@"bookmark_bar_shadow"];
-  UIImageView* shadow =
-      [[[UIImageView alloc] initWithImage:shadowImage] autorelease];
+  UIImageView* shadow = [[UIImageView alloc] initWithImage:shadowImage];
   CGRect shadowFrame = CGRectMake(0, 0, width, 4);
   shadow.frame = shadowFrame;
   shadow.autoresizingMask =
@@ -225,8 +226,8 @@
   }
 
   // Secondly, create an Undo group for all undoable actions.
-  base::scoped_nsobject<UndoManagerWrapper> wrapper(
-      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state]);
+  UndoManagerWrapper* wrapper =
+      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state];
 
   // Create or update the bookmark.
   [wrapper startGroupingActions];
@@ -260,8 +261,7 @@
 
 void PresentUndoToastWithWrapper(UndoManagerWrapper* wrapper, NSString* text) {
   // Create the block that will be executed if the user taps the undo button.
-  MDCSnackbarMessageAction* action =
-      [[[MDCSnackbarMessageAction alloc] init] autorelease];
+  MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init];
   action.handler = ^{
     if (![wrapper hasUndoManagerChanged])
       [wrapper undo];
@@ -302,8 +302,8 @@
   size_t nodeCount = nodes.size();
   DCHECK_GT(nodeCount, 0u);
 
-  base::scoped_nsobject<UndoManagerWrapper> wrapper(
-      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state]);
+  UndoManagerWrapper* wrapper =
+      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state];
 
   // Delete the selected bookmarks.
   [wrapper startGroupingActions];
@@ -354,8 +354,8 @@
   size_t nodeCount = nodes.size();
   DCHECK_GT(nodeCount, 0u);
 
-  base::scoped_nsobject<UndoManagerWrapper> wrapper(
-      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state]);
+  UndoManagerWrapper* wrapper =
+      [[UndoManagerWrapper alloc] initWithBrowserState:browser_state];
 
   // Move the selected bookmarks.
   [wrapper startGroupingActions];
@@ -404,38 +404,39 @@
   nodesSectionVector.clear();
 
   // Make a localized date formatter.
-  base::scoped_nsobject<NSDateFormatter> formatter(
-      [[NSDateFormatter alloc] init]);
+  NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
   [formatter setDateFormat:@"MMMM yyyy"];
   // Segregate nodes by creation date.
   // Nodes that were created in the same month are grouped together.
   for (auto* node : vector) {
-    base::mac::ScopedNSAutoreleasePool pool;
-    base::Time dateAdded = node->date_added();
-    base::TimeDelta delta = dateAdded - base::Time::UnixEpoch();
-    base::scoped_nsobject<NSDate> date(
-        [[NSDate alloc] initWithTimeIntervalSince1970:delta.InSeconds()]);
-    NSString* dateString = [formatter stringFromDate:date];
-    const std::string timeRepresentation = base::SysNSStringToUTF8(dateString);
+    @autoreleasepool {
+      base::Time dateAdded = node->date_added();
+      base::TimeDelta delta = dateAdded - base::Time::UnixEpoch();
+      NSDate* date =
+          [[NSDate alloc] initWithTimeIntervalSince1970:delta.InSeconds()];
+      NSString* dateString = [formatter stringFromDate:date];
+      const std::string timeRepresentation =
+          base::SysNSStringToUTF8(dateString);
 
-    BOOL found = NO;
-    for (const auto& nodesSection : nodesSectionVector) {
-      if (nodesSection->timeRepresentation == timeRepresentation) {
-        nodesSection->vector.push_back(node);
-        found = YES;
-        break;
+      BOOL found = NO;
+      for (const auto& nodesSection : nodesSectionVector) {
+        if (nodesSection->timeRepresentation == timeRepresentation) {
+          nodesSection->vector.push_back(node);
+          found = YES;
+          break;
+        }
       }
+
+      if (found)
+        continue;
+
+      // No NodesSection found.
+      auto nodesSection = base::MakeUnique<NodesSection>();
+      nodesSection->time = dateAdded;
+      nodesSection->timeRepresentation = timeRepresentation;
+      nodesSection->vector.push_back(node);
+      nodesSectionVector.push_back(std::move(nodesSection));
     }
-
-    if (found)
-      continue;
-
-    // No NodesSection found.
-    auto nodesSection = base::MakeUnique<NodesSection>();
-    nodesSection->time = dateAdded;
-    nodesSection->timeRepresentation = timeRepresentation;
-    nodesSection->vector.push_back(node);
-    nodesSectionVector.push_back(std::move(nodesSection));
   }
 
   // Sort the NodesSections.
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios_unittest.mm b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios_unittest.mm
index d9ec4ef..7edfa44 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios_unittest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios_unittest.mm
@@ -15,6 +15,10 @@
 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
 #include "testing/gtest_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using bookmarks::BookmarkNode;
 
 namespace {
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index e513818..f2f1b10 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -39,6 +39,7 @@
     "reading_list_collection_view_controller.mm",
     "reading_list_collection_view_item.h",
     "reading_list_collection_view_item.mm",
+    "reading_list_collection_view_item_accessibility_delegate.h",
     "reading_list_coordinator.h",
     "reading_list_coordinator.mm",
     "reading_list_empty_collection_background.h",
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
index 627c577..45ddedc 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
@@ -25,10 +25,12 @@
 #import "ios/chrome/browser/ui/favicon_view.h"
 #import "ios/chrome/browser/ui/material_components/utils.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item_accessibility_delegate.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/browser/ui/url_loader.h"
+#import "ios/chrome/browser/ui/util/pasteboard_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
@@ -65,7 +67,8 @@
 }
 
 @interface ReadingListCollectionViewController ()<
-    ReadingListModelBridgeObserver> {
+    ReadingListModelBridgeObserver,
+    ReadingListCollectionViewItemAccessibilityDelegate> {
   // Toolbar with the actions.
   ReadingListToolbar* _toolbar;
   // Action sheet presenting the subactions of the toolbar.
@@ -116,6 +119,10 @@
 - (void)handleLongPress:(UILongPressGestureRecognizer*)gestureRecognizer;
 // Stops observing the ReadingListModel.
 - (void)stopObservingReadingListModel;
+// Returns the ReadingListEntry associated with the |item|. If there is not such
+// an entry, returns nullptr and reloads the UI.
+- (const ReadingListEntry*)readingListEntryForItem:
+    (ReadingListCollectionViewItem*)item;
 // Updates the toolbar state according to the selected items.
 - (void)updateToolbarState;
 // Displays an action sheet to let the user choose to mark all the elements as
@@ -134,7 +141,7 @@
 - (void)markItemsUnreadAtIndexPath:(NSArray*)indexPaths;
 // Deletes all the read items.
 - (void)deleteAllReadItems;
-// Deletes all the items at |indexPaths|.
+// Deletes all the items at |indexPath|.
 - (void)deleteItemsAtIndexPaths:(NSArray*)indexPaths;
 // Initializes |_actionSheet| with |self| as base view controller, and the
 // toolbar's mark button as anchor point.
@@ -392,6 +399,100 @@
   [_actionSheet stop];
 }
 
+#pragma mark - ReadingListCollectionViewItemAccessibilityDelegate
+
+- (BOOL)isEntryRead:(ReadingListCollectionViewItem*)entry {
+  const ReadingListEntry* readingListEntry =
+      [self readingListEntryForItem:entry];
+
+  if (!readingListEntry) {
+    return NO;
+  }
+
+  return readingListEntry->IsRead();
+}
+
+- (void)deleteEntry:(ReadingListCollectionViewItem*)entry {
+  const ReadingListEntry* readingListEntry =
+      [self readingListEntryForItem:entry];
+
+  if (!readingListEntry) {
+    return;
+  }
+
+  SectionIdentifier sectionIdentifier = SectionIdentifierUnread;
+  if (readingListEntry->IsRead()) {
+    sectionIdentifier = SectionIdentifierRead;
+  }
+  if (![self.collectionViewModel hasItem:entry
+                 inSectionWithIdentifier:sectionIdentifier]) {
+    return;
+  }
+
+  [self
+      deleteItemsAtIndexPaths:@[ [self.collectionViewModel
+                                         indexPathForItem:entry
+                                  inSectionWithIdentifier:sectionIdentifier] ]];
+}
+
+- (void)openEntryInNewTab:(ReadingListCollectionViewItem*)entry {
+  [self.delegate readingListCollectionViewController:self
+                                   openNewTabWithURL:entry.url
+                                           incognito:NO];
+}
+
+- (void)openEntryInNewIncognitoTab:(ReadingListCollectionViewItem*)entry {
+  [self.delegate readingListCollectionViewController:self
+                                   openNewTabWithURL:entry.url
+                                           incognito:YES];
+}
+
+- (void)copyEntryURL:(ReadingListCollectionViewItem*)entry {
+  StoreURLInPasteboard(entry.url);
+}
+
+- (void)openEntryOffline:(ReadingListCollectionViewItem*)entry {
+  const ReadingListEntry* readingListEntry =
+      [self readingListEntryForItem:entry];
+
+  if (!readingListEntry) {
+    return;
+  }
+
+  if (readingListEntry->DistilledState() == ReadingListEntry::PROCESSED) {
+    const GURL entryURL = readingListEntry->URL();
+    GURL offlineURL = reading_list::OfflineURLForPath(
+        readingListEntry->DistilledPath(), entryURL,
+        readingListEntry->DistilledURL());
+
+    [self.delegate readingListCollectionViewController:self
+                                        openOfflineURL:offlineURL
+                                 correspondingEntryURL:entryURL];
+  }
+}
+
+- (void)markEntryRead:(ReadingListCollectionViewItem*)entry {
+  if (![self.collectionViewModel hasItem:entry
+                 inSectionWithIdentifier:SectionIdentifierUnread]) {
+    return;
+  }
+  [self markItemsReadAtIndexPath:@[
+    [self.collectionViewModel indexPathForItem:entry
+                       inSectionWithIdentifier:SectionIdentifierUnread]
+  ]];
+}
+
+- (void)markEntryUnread:(ReadingListCollectionViewItem*)entry {
+  if (![self.collectionViewModel hasItem:entry
+                 inSectionWithIdentifier:SectionIdentifierRead]) {
+    return;
+  }
+  [self markItemsUnreadAtIndexPath:@[
+    [self.collectionViewModel indexPathForItem:entry
+                       inSectionWithIdentifier:SectionIdentifierRead]
+  ]];
+}
+
 #pragma mark - Private methods
 
 - (void)donePressed {
@@ -451,6 +552,7 @@
     const ReadingListEntry* entry = self.readingListModel->GetEntryByURL(url);
     ReadingListCollectionViewItem* item =
         [self cellItemForReadingListEntry:*entry];
+    item.accessibilityDelegate = self;
     if (entry->IsRead()) {
       read_map.insert(std::make_pair(entry->UpdateTime(), item));
     } else {
@@ -588,6 +690,20 @@
   _modelBridge.reset();
 }
 
+- (const ReadingListEntry*)readingListEntryForItem:
+    (ReadingListCollectionViewItem*)item {
+  const ReadingListEntry* readingListEntry =
+      self.readingListModel->GetEntryByURL(item.url);
+
+  if (!readingListEntry) {
+    // The entry has been removed from the model, reload all data to synchronize
+    // the UI with the model.
+    [self reloadData];
+  }
+
+  return readingListEntry;
+}
+
 #pragma mark - ReadingListToolbarDelegate
 
 - (void)markPressed {
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h
index 7adba7c..38e9434 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h
@@ -11,6 +11,7 @@
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
 class GURL;
+@protocol ReadingListCollectionViewItemAccessibilityDelegate;
 
 // Collection view item for representing a ReadingListEntry.
 @interface ReadingListCollectionViewItem : CollectionViewItem
@@ -26,6 +27,10 @@
 // Status of the offline version.
 @property(nonatomic, assign)
     ReadingListEntry::DistillationState distillationState;
+// Delegate for the accessibility actions.
+@property(nonatomic, weak)
+    id<ReadingListCollectionViewItemAccessibilityDelegate>
+        accessibilityDelegate;
 
 // Designated initializer. The |provider| will be used for loading favicon
 // attributes. The |delegate| is used to inform about changes to this item. The
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.mm b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.mm
index 9618ce4..b488546 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.mm
@@ -7,6 +7,7 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #import "ios/chrome/browser/ui/favicon_view.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item_accessibility_delegate.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
@@ -59,6 +60,10 @@
 // The cell that is displaying this item, if any. Used to reload favicon when
 // the cell is on screen. Backed by WeakNSObject.
 @property(nonatomic, weak) ReadingListCell* displayedCell;
+
+// Returns the accessibility custom actions associated with this item.
+- (NSArray<UIAccessibilityCustomAction*>*)customActions;
+
 @end
 
 @implementation ReadingListCollectionViewItem
@@ -70,6 +75,7 @@
 @synthesize faviconPageURL = _faviconPageURL;
 @synthesize displayedCell = _displayedCell;
 @synthesize distillationState = _distillationState;
+@synthesize accessibilityDelegate = _accessibilityDelegate;
 
 - (instancetype)initWithType:(NSInteger)type
           attributesProvider:(FaviconAttributesProvider*)provider
@@ -111,6 +117,7 @@
     (ReadingListEntry::DistillationState)distillationState {
   self.displayedCell.distillationState = distillationState;
   self.displayedCell.accessibilityLabel = [self accessibilityLabel];
+  self.displayedCell.accessibilityCustomActions = [self customActions];
   _distillationState = distillationState;
 }
 
@@ -128,6 +135,7 @@
   cell.distillationState = _distillationState;
   cell.isAccessibilityElement = YES;
   cell.accessibilityLabel = [self accessibilityLabel];
+  cell.accessibilityCustomActions = [self customActions];
 }
 
 #pragma mark - ReadingListCellDelegate
@@ -154,6 +162,102 @@
                                  base::SysNSStringToUTF16(self.detailText));
 }
 
+#pragma mark - AccessibilityCustomAction
+
+- (NSArray<UIAccessibilityCustomAction*>*)customActions {
+  UIAccessibilityCustomAction* deleteAction = [
+      [UIAccessibilityCustomAction alloc]
+      initWithName:l10n_util::GetNSString(IDS_IOS_READING_LIST_DELETE_BUTTON)
+            target:self
+          selector:@selector(deleteEntry)];
+  UIAccessibilityCustomAction* toogleReadStatus = nil;
+  if ([self.accessibilityDelegate isEntryRead:self]) {
+    toogleReadStatus = [[UIAccessibilityCustomAction alloc]
+        initWithName:l10n_util::GetNSString(
+                         IDS_IOS_READING_LIST_MARK_UNREAD_BUTTON)
+              target:self
+            selector:@selector(markUnread)];
+  } else {
+    toogleReadStatus = [[UIAccessibilityCustomAction alloc]
+        initWithName:l10n_util::GetNSString(
+                         IDS_IOS_READING_LIST_MARK_READ_BUTTON)
+              target:self
+            selector:@selector(markRead)];
+  }
+
+  UIAccessibilityCustomAction* openInNewTabAction =
+      [[UIAccessibilityCustomAction alloc]
+          initWithName:l10n_util::GetNSString(
+                           IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB)
+                target:self
+              selector:@selector(openInNewTab)];
+  UIAccessibilityCustomAction* openInNewIncognitoTabAction =
+      [[UIAccessibilityCustomAction alloc]
+          initWithName:l10n_util::GetNSString(
+                           IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB)
+                target:self
+              selector:@selector(openInNewIncognitoTab)];
+  UIAccessibilityCustomAction* copyURLAction =
+      [[UIAccessibilityCustomAction alloc]
+          initWithName:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY)
+                target:self
+              selector:@selector(copyURL)];
+
+  NSMutableArray* customActions = [NSMutableArray
+      arrayWithObjects:deleteAction, toogleReadStatus, openInNewTabAction,
+                       openInNewIncognitoTabAction, copyURLAction, nil];
+
+  if (self.distillationState == ReadingListEntry::PROCESSED) {
+    // Add the possibility to open offline version only if the entry is
+    // distilled.
+    UIAccessibilityCustomAction* openOfflineAction =
+        [[UIAccessibilityCustomAction alloc]
+            initWithName:l10n_util::GetNSString(
+                             IDS_IOS_READING_LIST_CONTENT_CONTEXT_OFFLINE)
+                  target:self
+                selector:@selector(openOffline)];
+
+    [customActions addObject:openOfflineAction];
+  }
+
+  return customActions;
+}
+
+- (BOOL)deleteEntry {
+  [self.accessibilityDelegate deleteEntry:self];
+  return YES;
+}
+
+- (BOOL)markRead {
+  [self.accessibilityDelegate markEntryRead:self];
+  return YES;
+}
+
+- (BOOL)markUnread {
+  [self.accessibilityDelegate markEntryUnread:self];
+  return YES;
+}
+
+- (BOOL)openInNewTab {
+  [self.accessibilityDelegate openEntryInNewTab:self];
+  return YES;
+}
+
+- (BOOL)openInNewIncognitoTab {
+  [self.accessibilityDelegate openEntryInNewIncognitoTab:self];
+  return YES;
+}
+
+- (BOOL)copyURL {
+  [self.accessibilityDelegate copyEntryURL:self];
+  return YES;
+}
+
+- (BOOL)openOffline {
+  [self.accessibilityDelegate openEntryOffline:self];
+  return YES;
+}
+
 #pragma mark - NSObject
 
 - (NSString*)description {
@@ -281,6 +385,7 @@
   self.textLabel.text = nil;
   self.detailTextLabel.text = nil;
   self.distillationState = ReadingListEntry::WAITING;
+  self.accessibilityCustomActions = nil;
   [super prepareForReuse];
 }
 
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item_accessibility_delegate.h b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item_accessibility_delegate.h
new file mode 100644
index 0000000..a9e04a2e
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_item_accessibility_delegate.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COLLECTION_VIEW_ITEM_ACCESSIBILITY_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COLLECTION_VIEW_ITEM_ACCESSIBILITY_DELEGATE_H_
+
+@class ReadingListCollectionViewItem;
+
+@protocol ReadingListCollectionViewItemAccessibilityDelegate
+
+// Returns whether the entry is read.
+- (BOOL)isEntryRead:(ReadingListCollectionViewItem*)entry;
+
+- (void)deleteEntry:(ReadingListCollectionViewItem*)entry;
+- (void)openEntryInNewTab:(ReadingListCollectionViewItem*)entry;
+- (void)openEntryInNewIncognitoTab:(ReadingListCollectionViewItem*)entry;
+- (void)copyEntryURL:(ReadingListCollectionViewItem*)entry;
+- (void)openEntryOffline:(ReadingListCollectionViewItem*)entry;
+- (void)markEntryRead:(ReadingListCollectionViewItem*)entry;
+- (void)markEntryUnread:(ReadingListCollectionViewItem*)entry;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COLLECTION_VIEW_ITEM_ACCESSIBILITY_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/webui/physical_web_ui.cc b/ios/chrome/browser/ui/webui/physical_web_ui.cc
index 98a5ccef..6a8b7d4 100644
--- a/ios/chrome/browser/ui/webui/physical_web_ui.cc
+++ b/ios/chrome/browser/ui/webui/physical_web_ui.cc
@@ -28,7 +28,8 @@
   // Localized and data strings.
   html_source->AddLocalizedString(physical_web_ui::kTitle,
                                   IDS_PHYSICAL_WEB_UI_TITLE);
-
+  html_source->AddLocalizedString(physical_web_ui::kEmptyMessage,
+                                  IDS_PHYSICAL_WEB_UI_EMPTY_MESSAGE);
   html_source->SetJsonPath("strings.js");
   html_source->AddResourcePath(physical_web_ui::kPhysicalWebJS,
                                IDR_PHYSICAL_WEB_UI_JS);
diff --git a/ios/clean/chrome/browser/browser_coordinator+internal.h b/ios/clean/chrome/browser/browser_coordinator+internal.h
index 0fdec93..06315554a 100644
--- a/ios/clean/chrome/browser/browser_coordinator+internal.h
+++ b/ios/clean/chrome/browser/browser_coordinator+internal.h
@@ -55,6 +55,16 @@
 // |coordinator| isn't a child of the receiver, this method does nothing.
 - (void)removeChildCoordinator:(BrowserCoordinator*)coordinator;
 
+// Called when a child coordinator did start. This is a blank template method.
+// Subclasses can override this method when they need to know when their
+// children start.
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator;
+
+// Called when a child coordinator will stop. This is a blank template method.
+// Subclasses can override this method when they need to know when their
+// children start.
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator;
+
 // Methods for adding overlay coordinators.
 
 // Returns YES if the receiver will take |overlayCoordinator| as a child.
diff --git a/ios/clean/chrome/browser/browser_coordinator.h b/ios/clean/chrome/browser/browser_coordinator.h
index 6b06543..0cb5aeea0 100644
--- a/ios/clean/chrome/browser/browser_coordinator.h
+++ b/ios/clean/chrome/browser/browser_coordinator.h
@@ -30,15 +30,19 @@
 @property(nonatomic, assign) Browser* browser;
 
 // The basic lifecycle methods for coordinators are -start and -stop. These
-// are blank template methods; child classes are expected to implement them and
-// do not need to invoke the superclass methods.
+// implementations only notify the parent coordinator when this coordinator did
+// start and will stop. Child classes are expected to override and call the
+// superclass method at the end of -start and at the beginning of -stop.
+
 // Starts the user interaction managed by the receiver. Typical implementations
 // will create a view controller and then use |baseViewController| to present
-// it.
-- (void)start;
+// it. This method needs to be called at the end of the overriding
+// implementation.
+- (void)start NS_REQUIRES_SUPER;
 
-// Stops the user interaction managed by the receiver.
-- (void)stop;
+// Stops the user interaction managed by the receiver. This method needs to be
+// called at the beginning of the overriding implementation.
+- (void)stop NS_REQUIRES_SUPER;
 
 @end
 
diff --git a/ios/clean/chrome/browser/browser_coordinator.mm b/ios/clean/chrome/browser/browser_coordinator.mm
index 4c7af01..828ae8d 100644
--- a/ios/clean/chrome/browser/browser_coordinator.mm
+++ b/ios/clean/chrome/browser/browser_coordinator.mm
@@ -43,11 +43,11 @@
 #pragma mark - Public API
 
 - (void)start {
-  // Default implementation is a no-op.
+  [self.parentCoordinator childCoordinatorDidStart:self];
 }
 
 - (void)stop {
-  // Default implementation is a no-op.
+  [self.parentCoordinator childCoordinatorWillStop:self];
 }
 
 @end
@@ -122,4 +122,12 @@
   coordinator.parentCoordinator = nil;
 }
 
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator {
+  // Default implementation is a no-op.
+}
+
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator {
+  // Default implementation is a no-op.
+}
+
 @end
diff --git a/ios/clean/chrome/browser/browser_coordinator_unittest.mm b/ios/clean/chrome/browser/browser_coordinator_unittest.mm
index 54fbd76..88f0186 100644
--- a/ios/clean/chrome/browser/browser_coordinator_unittest.mm
+++ b/ios/clean/chrome/browser/browser_coordinator_unittest.mm
@@ -31,6 +31,7 @@
 }
 
 - (void)stop {
+  [super stop];
   if (self.stopHandler)
     self.stopHandler();
 }
diff --git a/ios/clean/chrome/browser/ui/commands/BUILD.gn b/ios/clean/chrome/browser/ui/commands/BUILD.gn
index 8b12cb4..6ad215d 100644
--- a/ios/clean/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/commands/BUILD.gn
@@ -4,11 +4,30 @@
 
 source_set("commands") {
   sources = [
+    "command_dispatcher.h",
+    "command_dispatcher.mm",
     "settings_commands.h",
     "tab_commands.h",
     "tab_grid_commands.h",
     "toolbar_commands.h",
   ]
+  deps = [
+    "//base",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "command_dispatcher_unittest.mm",
+  ]
+  deps = [
+    ":commands",
+    "//base",
+    "//testing/gtest",
+  ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/clean/chrome/browser/ui/commands/command_dispatcher.h b/ios/clean/chrome/browser/ui/commands/command_dispatcher.h
new file mode 100644
index 0000000..753542f
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/command_dispatcher.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_COMMAND_DISPATCHER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_COMMAND_DISPATCHER_H_
+
+#import <Foundation/Foundation.h>
+
+// CommandDispatcher allows coordinators to register as command handlers for
+// specific selectors.  Other objects can call these methods on the dispatcher,
+// which in turn will forward the call to the registered handler.
+@interface CommandDispatcher : NSObject
+
+// Registers the given |target| to receive forwarded messages for the given
+// |selector|.
+- (void)registerTarget:(id)target forSelector:(SEL)selector;
+
+// Removes all forwarding registrations for the given |target|.
+- (void)stopDispatchingForTarget:(id)target;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_COMMAND_DISPATCHER_H_
diff --git a/ios/clean/chrome/browser/ui/commands/command_dispatcher.mm b/ios/clean/chrome/browser/ui/commands/command_dispatcher.mm
new file mode 100644
index 0000000..23133a3
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/command_dispatcher.mm
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/commands/command_dispatcher.h"
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CommandDispatcher {
+  // Stores which target to forward to for a given selector.
+  std::unordered_map<SEL, __weak id> _forwardingTargets;
+}
+
+- (void)registerTarget:(id)target forSelector:(SEL)selector {
+  DCHECK(_forwardingTargets.find(selector) == _forwardingTargets.end());
+
+  _forwardingTargets[selector] = target;
+}
+
+// |-stopDispatchingForTarget| should be called much less often than
+// |-forwardingTargetForSelector|, so removal is intentionally O(n) in order
+// to prioritize the speed of lookups.
+- (void)stopDispatchingForTarget:(id)target {
+  std::vector<SEL> selectorsToErase;
+  for (auto& kv : _forwardingTargets) {
+    if (kv.second == target) {
+      selectorsToErase.push_back(kv.first);
+    }
+  }
+
+  for (auto* selector : selectorsToErase) {
+    _forwardingTargets.erase(selector);
+  }
+}
+
+#pragma mark - NSObject
+
+// Overridden to forward messages to registered handlers.
+- (id)forwardingTargetForSelector:(SEL)selector {
+  auto target = _forwardingTargets.find(selector);
+  if (target != _forwardingTargets.end()) {
+    return target->second;
+  }
+  return [super forwardingTargetForSelector:selector];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/commands/command_dispatcher_unittest.mm b/ios/clean/chrome/browser/ui/commands/command_dispatcher_unittest.mm
new file mode 100644
index 0000000..3154e53
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/command_dispatcher_unittest.mm
@@ -0,0 +1,233 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/commands/command_dispatcher.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#pragma mark - Test handlers
+
+// A handler with methods that take no arguments.
+@interface CommandDispatcherTestSimpleTarget : NSObject
+
+// Will be set to YES when the |-show| method is called.
+@property(nonatomic, assign) BOOL showCalled;
+
+// Will be set to YES when the |-hide| method is called.
+@property(nonatomic, assign) BOOL hideCalled;
+
+// Resets the above properties to NO.
+- (void)resetProperties;
+
+// Handler methods.
+- (void)show;
+- (void)hide;
+
+@end
+
+@implementation CommandDispatcherTestSimpleTarget
+
+@synthesize showCalled = _showCalled;
+@synthesize hideCalled = _hideCalled;
+
+- (void)resetProperties {
+  self.showCalled = NO;
+  self.hideCalled = NO;
+}
+
+- (void)show {
+  self.showCalled = YES;
+}
+
+- (void)hide {
+  self.hideCalled = YES;
+}
+
+@end
+
+// A handler with methods that take various types of arguments.
+@interface CommandDispatcherTestTargetWithArguments : NSObject
+
+// Set to YES when |-methodWithInt:| is called.
+@property(nonatomic, assign) BOOL intMethodCalled;
+
+// The argument passed to the most recent call of |-methodWithInt:|.
+@property(nonatomic, assign) int intArgument;
+
+// Set to YES when |-methodWithObject:| is called.
+@property(nonatomic, assign) BOOL objectMethodCalled;
+
+// The argument passed to the most recent call of |-methodWithObject:|.
+@property(nonatomic, strong) NSObject* objectArgument;
+
+// Resets the above properties to NO or nil.
+- (void)resetProperties;
+
+// Handler methods.
+- (void)methodWithInt:(int)arg;
+- (void)methodWithObject:(NSObject*)arg;
+- (int)methodToAddFirstArgument:(int)first toSecond:(int)second;
+
+@end
+
+@implementation CommandDispatcherTestTargetWithArguments
+
+@synthesize intMethodCalled = _intMethodCalled;
+@synthesize intArgument = _intArgument;
+@synthesize objectMethodCalled = _objectMethodCalled;
+@synthesize objectArgument = _objectArgument;
+
+- (void)resetProperties {
+  self.intMethodCalled = NO;
+  self.intArgument = 0;
+  self.objectMethodCalled = NO;
+  self.objectArgument = nil;
+}
+
+- (void)methodWithInt:(int)arg {
+  self.intMethodCalled = YES;
+  self.intArgument = arg;
+}
+
+- (void)methodWithObject:(NSObject*)arg {
+  self.objectMethodCalled = YES;
+  self.objectArgument = arg;
+}
+
+- (int)methodToAddFirstArgument:(int)first toSecond:(int)second {
+  return first + second;
+}
+
+@end
+
+#pragma mark - Tests
+
+// Tests handler methods with no arguments.
+TEST(CommandDispatcherTest, SimpleTarget) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* target =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  [dispatcher registerTarget:target forSelector:@selector(show)];
+  [dispatcher registerTarget:target forSelector:@selector(hide)];
+
+  [dispatcher show];
+  EXPECT_TRUE(target.showCalled);
+  EXPECT_FALSE(target.hideCalled);
+
+  [target resetProperties];
+  [dispatcher hide];
+  EXPECT_FALSE(target.showCalled);
+  EXPECT_TRUE(target.hideCalled);
+}
+
+// Tests handler methods that take arguments.
+TEST(CommandDispatcherTest, TargetWithArguments) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestTargetWithArguments* target =
+      [[CommandDispatcherTestTargetWithArguments alloc] init];
+
+  [dispatcher registerTarget:target forSelector:@selector(methodWithInt:)];
+  [dispatcher registerTarget:target forSelector:@selector(methodWithObject:)];
+  [dispatcher registerTarget:target
+                 forSelector:@selector(methodToAddFirstArgument:toSecond:)];
+
+  const int int_argument = 4;
+  [dispatcher methodWithInt:int_argument];
+  EXPECT_TRUE(target.intMethodCalled);
+  EXPECT_FALSE(target.objectMethodCalled);
+  EXPECT_EQ(int_argument, target.intArgument);
+
+  [target resetProperties];
+  NSObject* object_argument = [[NSObject alloc] init];
+  [dispatcher methodWithObject:object_argument];
+  EXPECT_FALSE(target.intMethodCalled);
+  EXPECT_TRUE(target.objectMethodCalled);
+  EXPECT_EQ(object_argument, target.objectArgument);
+
+  [target resetProperties];
+  EXPECT_EQ(13, [dispatcher methodToAddFirstArgument:7 toSecond:6]);
+  EXPECT_FALSE(target.intMethodCalled);
+  EXPECT_FALSE(target.objectMethodCalled);
+  EXPECT_EQ(42, [dispatcher methodToAddFirstArgument:34 toSecond:8]);
+}
+
+// Tests that messages are routed to the proper handler when multiple targets
+// are registered.
+TEST(CommandDispatcherTest, MultipleTargets) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* showTarget =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+  CommandDispatcherTestSimpleTarget* hideTarget =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  [dispatcher registerTarget:showTarget forSelector:@selector(show)];
+  [dispatcher registerTarget:hideTarget forSelector:@selector(hide)];
+
+  [dispatcher show];
+  EXPECT_TRUE(showTarget.showCalled);
+  EXPECT_FALSE(hideTarget.showCalled);
+
+  [showTarget resetProperties];
+  [dispatcher hide];
+  EXPECT_FALSE(showTarget.hideCalled);
+  EXPECT_TRUE(hideTarget.hideCalled);
+}
+
+// Tests that handlers are no longer forwarded messages after deregistration.
+TEST(CommandDispatcherTest, Deregistration) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* showTarget =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+  CommandDispatcherTestSimpleTarget* hideTarget =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  [dispatcher registerTarget:showTarget forSelector:@selector(show)];
+  [dispatcher registerTarget:hideTarget forSelector:@selector(hide)];
+
+  [dispatcher show];
+  EXPECT_TRUE(showTarget.showCalled);
+  EXPECT_FALSE(hideTarget.showCalled);
+
+  [dispatcher stopDispatchingForTarget:showTarget];
+  bool exception_caught = false;
+  @try {
+    [dispatcher show];
+  } @catch (NSException* exception) {
+    EXPECT_EQ(NSInvalidArgumentException, [exception name]);
+    exception_caught = true;
+  }
+  EXPECT_TRUE(exception_caught);
+
+  [dispatcher hide];
+  EXPECT_FALSE(showTarget.hideCalled);
+  EXPECT_TRUE(hideTarget.hideCalled);
+}
+
+// Tests that an exception is thrown when there is no registered handler for a
+// given selector.
+TEST(CommandDispatcherTest, NoTargetRegisteredForSelector) {
+  id dispatcher = [[CommandDispatcher alloc] init];
+  CommandDispatcherTestSimpleTarget* target =
+      [[CommandDispatcherTestSimpleTarget alloc] init];
+
+  [dispatcher registerTarget:target forSelector:@selector(show)];
+
+  bool exception_caught = false;
+  @try {
+    [dispatcher hide];
+  } @catch (NSException* exception) {
+    EXPECT_EQ(NSInvalidArgumentException, [exception name]);
+    exception_caught = true;
+  }
+
+  EXPECT_TRUE(exception_caught);
+}
diff --git a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 4e61776..3be9962 100644
--- a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -23,6 +23,7 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 @end
diff --git a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
index 536bebce..46a0fc98 100644
--- a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
@@ -38,9 +38,11 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 - (void)stop {
+  [super stop];
   [self.viewController.presentingViewController
       dismissViewControllerAnimated:self.context.animated
                          completion:nil];
diff --git a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
index 3ccbaa6..a8219710 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
@@ -71,9 +71,6 @@
   toolbarCoordinator.context.baseViewController = nil;
   [toolbarCoordinator start];
 
-  self.viewController.toolbarViewController = toolbarCoordinator.viewController;
-  self.viewController.contentViewController = webCoordinator.viewController;
-
   // PLACEHOLDER: Replace this placeholder with an actual tab strip view
   // controller.
   UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
@@ -91,15 +88,25 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 - (void)stop {
+  [super stop];
   [self.viewController.presentingViewController
       dismissViewControllerAnimated:self.context.animated
                          completion:nil];
   _webStateObserver.reset();
 }
 
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)coordinator {
+  if ([coordinator isKindOfClass:[ToolbarCoordinator class]]) {
+    self.viewController.toolbarViewController = coordinator.viewController;
+  } else if ([coordinator isKindOfClass:[WebCoordinator class]]) {
+    self.viewController.contentViewController = coordinator.viewController;
+  }
+}
+
 - (BOOL)canAddOverlayCoordinator:(BrowserCoordinator*)overlayCoordinator {
   // This coordinator will always accept overlay coordinators.
   return YES;
diff --git a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index d36633a..1b620951 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -75,6 +75,7 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 #pragma mark - TabGridDataSource
diff --git a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_container_coordinator.mm b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_container_coordinator.mm
index cec30fa..932ed19 100644
--- a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_container_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_container_coordinator.mm
@@ -64,9 +64,11 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 - (void)stop {
+  [super stop];
   [self.viewController.presentingViewController
       dismissViewControllerAnimated:self.context.animated
                          completion:nil];
diff --git a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index 8bda941..41a3fec8 100644
--- a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -51,6 +51,7 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 #pragma mark - ToolbarCommands
diff --git a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
index d80614ef..aaec5a47 100644
--- a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
@@ -35,9 +35,11 @@
   [self.context.baseViewController presentViewController:self.menuViewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 - (void)stop {
+  [super stop];
   [self.menuViewController.presentingViewController
       dismissViewControllerAnimated:self.context.animated
                          completion:nil];
diff --git a/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm b/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
index f736feb..0248d70 100644
--- a/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
@@ -50,9 +50,11 @@
   [self.context.baseViewController presentViewController:self.viewController
                                                 animated:self.context.animated
                                               completion:nil];
+  [super start];
 }
 
 - (void)stop {
+  [super stop];
   // PLACEHOLDER: This is how the webUsageEnabled is set to false. Find a
   // better way in the future.
   self.mediator.webState = nullptr;
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn
index 758c428..88d9e82 100644
--- a/ios/clean/chrome/test/BUILD.gn
+++ b/ios/clean/chrome/test/BUILD.gn
@@ -21,6 +21,7 @@
     # Add unit_tests target here.
     "//ios/clean/chrome/browser:unit_tests",
     "//ios/clean/chrome/browser/model:unit_tests",
+    "//ios/clean/chrome/browser/ui/commands:unit_tests",
     "//ios/clean/chrome/browser/ui/web_contents:unit_tests",
   ]
 }
diff --git a/net/interfaces/BUILD.gn b/net/interfaces/BUILD.gn
index ba7b53f..65910368 100644
--- a/net/interfaces/BUILD.gn
+++ b/net/interfaces/BUILD.gn
@@ -7,6 +7,7 @@
 mojom("interfaces") {
   sources = [
     "host_resolver_service.mojom",
+    "ip_address.mojom",
     "proxy_resolver_service.mojom",
   ]
   public_deps = [
diff --git a/net/interfaces/OWNERS b/net/interfaces/OWNERS
index 08850f4..2c44a46 100644
--- a/net/interfaces/OWNERS
+++ b/net/interfaces/OWNERS
@@ -1,2 +1,6 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/net/interfaces/ip_address.mojom b/net/interfaces/ip_address.mojom
new file mode 100644
index 0000000..012b81c
--- /dev/null
+++ b/net/interfaces/ip_address.mojom
@@ -0,0 +1,12 @@
+// 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.
+
+module net.interfaces;
+
+// Mirror of net::IPAddress.
+struct IPAddress {
+   // IP address as a numeric value from most to least significant byte.
+   // Will be of length 4 for IPv4 addresses and 16 for IPv6.
+   array<uint8> address;
+};
diff --git a/net/interfaces/ip_address.typemap b/net/interfaces/ip_address.typemap
new file mode 100644
index 0000000..64488d6
--- /dev/null
+++ b/net/interfaces/ip_address.typemap
@@ -0,0 +1,14 @@
+# 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.
+
+mojom = "//net/interfaces/ip_address.mojom"
+public_headers = [ "//net/base/ip_address.h" ]
+traits_headers = [ "//net/interfaces/ip_address_struct_traits.h" ]
+sources = [
+  "//net/interfaces/ip_address_struct_traits.cc",
+]
+type_mappings = [ "net.interfaces.IPAddress=net::IPAddress" ]
+public_deps = [
+  "//net",
+]
diff --git a/net/interfaces/ip_address_struct_traits.cc b/net/interfaces/ip_address_struct_traits.cc
new file mode 100644
index 0000000..b23ac88
--- /dev/null
+++ b/net/interfaces/ip_address_struct_traits.cc
@@ -0,0 +1,20 @@
+// 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 "net/interfaces/ip_address_struct_traits.h"
+
+namespace mojo {
+// static
+bool StructTraits<net::interfaces::IPAddressDataView, net::IPAddress>::Read(
+    net::interfaces::IPAddressDataView data,
+    net::IPAddress* out) {
+  std::vector<uint8_t> bytes;
+  if (!data.ReadAddress(&bytes))
+    return false;
+
+  *out = net::IPAddress(bytes);
+  return out->IsValid();
+}
+
+}  // namespace mojo
diff --git a/net/interfaces/ip_address_struct_traits.h b/net/interfaces/ip_address_struct_traits.h
new file mode 100644
index 0000000..9c97baf
--- /dev/null
+++ b/net/interfaces/ip_address_struct_traits.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_INTERFACES_IP_ADDRESS_STRUCT_TRAITS_H_
+#define NET_INTERFACES_IP_ADDRESS_STRUCT_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "net/base/ip_address.h"
+#include "net/interfaces/ip_address.mojom.h"
+
+namespace mojo {
+template <>
+struct StructTraits<net::interfaces::IPAddressDataView, net::IPAddress> {
+  static const std::vector<uint8_t>& address(const net::IPAddress& ip_address) {
+    return ip_address.bytes();
+  }
+
+  static bool Read(net::interfaces::IPAddressDataView obj, net::IPAddress* out);
+};
+
+}  // namespace mojo
+
+#endif  // NET_INTERFACES_IP_ADDRESS_STRUCT_TRAITS_H_
diff --git a/net/interfaces/typemaps.gni b/net/interfaces/typemaps.gni
index 57a75bf..7d8ec79 100644
--- a/net/interfaces/typemaps.gni
+++ b/net/interfaces/typemaps.gni
@@ -4,5 +4,6 @@
 
 typemaps = [
   "//net/interfaces/host_resolver.typemap",
+  "//net/interfaces/ip_address.typemap",
   "//net/interfaces/proxy_resolver.typemap",
 ]
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index d7192a2d..7a3ef422 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -1563,6 +1563,55 @@
   EXPECT_EQ(gfx::Point(35, 5), dispatched_event->location());
 }
 
+// Tests events on a sub-window of system modal window target the window itself.
+TEST_F(EventDispatcherTest, ModalWindowEventSubWindowSystemModal) {
+  std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+  w1->SetModalType(MODAL_TYPE_SYSTEM);
+  event_dispatcher()->AddSystemModalWindow(w1.get());
+
+  std::unique_ptr<ServerWindow> w2 =
+      CreateChildWindowWithParent(WindowId(1, 4), w1.get());
+  std::unique_ptr<ServerWindow> w3 = CreateChildWindow(WindowId(1, 5));
+
+  root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+  w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+  w2->SetBounds(gfx::Rect(10, 10, 10, 10));
+  w3->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+  struct {
+    gfx::Point location;
+    ServerWindow* expected_target;
+  } kTouchData[] = {
+      // Touch on |w1| should go to |w1|.
+      {gfx::Point(11, 11), w1.get()},
+      // Touch on |w2| should go to |w2|.
+      {gfx::Point(25, 25), w2.get()},
+      // Touch on |w3| should go to |w1|.
+      {gfx::Point(11, 31), w1.get()},
+  };
+
+  for (size_t i = 0; i < arraysize(kTouchData); i++) {
+    // Send touch press and check that the expected target receives it.
+    event_dispatcher()->ProcessEvent(
+        ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_PRESSED,
+                                        kTouchData[i].location, 0,
+                                        base::TimeTicks())),
+        EventDispatcher::AcceleratorMatchPhase::ANY);
+    std::unique_ptr<DispatchedEventDetails> details =
+        test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+    ASSERT_TRUE(details) << " details is nullptr " << i;
+    EXPECT_EQ(kTouchData[i].expected_target, details->window);
+
+    // Release touch.
+    event_dispatcher()->ProcessEvent(
+        ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_RELEASED,
+                                        kTouchData[i].location, 0,
+                                        base::TimeTicks())),
+        EventDispatcher::AcceleratorMatchPhase::ANY);
+    test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+  }
+}
+
 // Tests that setting capture to a descendant of a modal parent fails.
 TEST_F(EventDispatcherTest, ModalWindowSetCaptureDescendantOfModalParent) {
   std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
diff --git a/services/ui/ws/modal_window_controller.cc b/services/ui/ws/modal_window_controller.cc
index bb1c0bf..9ac70b9 100644
--- a/services/ui/ws/modal_window_controller.cc
+++ b/services/ui/ws/modal_window_controller.cc
@@ -78,16 +78,16 @@
 
 bool ModalWindowController::IsWindowBlocked(const ServerWindow* window) const {
   DCHECK(window);
-  return GetActiveSystemModalWindow() || GetModalChildForWindowAncestor(window);
+  return GetTargetForWindow(window) != window;
 }
 
 const ServerWindow* ModalWindowController::GetTargetForWindow(
     const ServerWindow* window) const {
-  // TODO(moshayedi): crbug.com/697127. Handle windows which are modal to
-  // children of their transient parent.
+  // TODO(moshayedi): crbug.com/697127. Handle MODAL_TYPE_CHILD.
   ServerWindow* system_modal_window = GetActiveSystemModalWindow();
   if (system_modal_window)
-    return system_modal_window;
+    return system_modal_window->Contains(window) ? window : system_modal_window;
+
   return window ? GetWindowModalTargetForWindow(window) : nullptr;
 }
 
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index bfca386..ddf9de8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1183,7 +1183,8 @@
       {
         "args": [
           "--browser=android-chromium",
-          "--device=android"
+          "--device=android",
+          "--skip=benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.*"
         ],
         "isolate_name": "telemetry_perf_unittests",
         "name": "telemetry_perf_unittests",
@@ -1205,6 +1206,18 @@
       }
     ]
   },
+  "Android arm Builder (dbg)": {
+    "additional_compile_targets": [
+      "dump_syms",
+      "microdump_stackwalk"
+    ]
+  },
+  "Android arm64 Builder (dbg)": {
+    "additional_compile_targets": [
+      "dump_syms",
+      "microdump_stackwalk"
+    ]
+  },
   "KitKat Tablet Tester": {
     "gtest_tests": [
       {
@@ -2786,6 +2799,41 @@
         "test": "components_browsertests"
       },
       {
+        "args": [
+          "--test-arguments=--enable-browser-side-navigation"
+        ],
+        "name": "browser_side_navigation_components_browsertests",
+        "override_isolate_target": "components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
         "override_isolate_target": "components_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -2817,6 +2865,76 @@
         "test": "components_unittests"
       },
       {
+        "args": [
+          "--test-arguments=--enable-browser-side-navigation"
+        ],
+        "name": "browser_side_navigation_components_unittests",
+        "override_isolate_target": "components_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "override_isolate_target": "content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-arguments=--enable-browser-side-navigation"
+        ],
+        "name": "browser_side_navigation_content_browsertests",
         "override_isolate_target": "content_browsertests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -2913,6 +3031,41 @@
         "test": "content_unittests"
       },
       {
+        "args": [
+          "--test-arguments=--enable-browser-side-navigation"
+        ],
+        "name": "browser_side_navigation_content_unittests",
+        "override_isolate_target": "content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
         "override_isolate_target": "device_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -3443,6 +3596,41 @@
         "test": "unit_tests"
       },
       {
+        "args": [
+          "--test-arguments=--enable-browser-side-navigation"
+        ],
+        "name": "browser_side_navigation_unit_tests",
+        "override_isolate_target": "unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:dec8cc6fd715753846d0aca1693dc63844ea55d6"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
         "override_isolate_target": "vr_shell_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index dc42690..55813f1f 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -421,6 +421,10 @@
     "label": "//services/ui/display:display_service_unittests",
     "type": "console_test_launcher",
   },
+  "dump_syms" : {
+    "label": "//breakpad:dump_syms",
+    "type": "additional_compile_target",
+  },
   "events_unittests": {
     "label": "//ui/events:events_unittests",
     "type": "windowed_test_launcher",
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 72744530..02a06a68 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -35,7 +35,6 @@
 # existing tests. We have skipped the entire spv2 directory and are adding [ Pass ] lines
 # as we go. Please remove [ Pass ] lines instead of rebaselining these tests.
 crbug.com/537409 virtual/spv2/ [ Skip ]
-crbug.com/563667 virtual/spv2/fast/block/basic/001.html [ Pass ]
 # Begin whitlelisted paint invalidation tests
 crbug.com/647831 virtual/spv2/paint/invalidation/margin.html [ Pass ]
 # End whitlelisted paint invalidation tests
@@ -101,7 +100,7 @@
 crbug.com/632000 [ Win ] images/paint-subrect-grid.html [ Pass Failure ]
 
 # Very slight (~2 pixel) differences.
-crbug.com/699573 [ Win ] virtual/gpu-rasterization/images/cross-fade-background-size.html [ Failure ]
+crbug.com/699573 [ Win ] virtual/gpu-rasterization/images/cross-fade-background-size.html [ Pass Failure ]
 
 crbug.com/639147 svg/wicd/test-rightsizing-b.xhtml [ Failure Pass ]
 
@@ -2227,6 +2226,603 @@
 crbug.com/658997 external/wpt/service-workers/service-worker/invalid-header.https.html [ Skip ]
 crbug.com/658997 external/wpt/service-workers/service-worker/referer.https.html [ Skip ]
 
+# The Mac10.12 builder was just added, and proper baselines or expectations
+# are not yet added for the below tests.
+crbug.com/697971 [ Mac10.12 ] compositing/overflow/border-radius-styles-with-composited-child.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] compositing/overflow/theme-affects-visual-overflow.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css2.1/t1202-counter-04-b.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css2.1/t1202-counters-04-b.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/flexbox/button.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-161.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-19b.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-23.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-24.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-64.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-68.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/html/css3-modsel-69.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-161.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-19b.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-23.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-24.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-64.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-68.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xhtml/css3-modsel-69.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-161.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-19b.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-23.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-24.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-64.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-68.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] css3/selectors3/xml/css3-modsel-69.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/caret/caret-color.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-at-paragraph-boundaries-011.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-004.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-005.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-006.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-007.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-008.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-009.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-010.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-011.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/deleting/delete-line-012.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/input/caret-at-the-edge-of-input.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/input/reveal-caret-of-multiline-input.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-003.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-004.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-005.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/inserting/insert-br-quoted-006.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/drop-text-without-selection.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/emacs-ctrl-k-y-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-line-endings-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-line-endings-002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-line-endings-003.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-line-endings-004.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-line-endings-005.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/paste-text-016.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/pasteboard/pasting-tabs.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/3690719.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/4975120.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/move-3875618-fix.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/move-3875641-fix.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/move-by-sentence-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/replaced-boundaries-3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-across-readonly-input-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-across-readonly-input-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-across-readonly-input-3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-across-readonly-input-4.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-across-readonly-input-5.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-element-paragraph-boundary.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/select-missing-image.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/selection/selection-button-text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/style/block-style-001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/style/block-style-002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/style/block-style-003.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] editing/style/block-styles-007.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/css-typed-om/CSSMatrixComponent-DOMMatrix-mutable.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/css-typed-om/styleMap-update-function.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/events/CustomEvent.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/events/Event-initEvent.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/interfaces.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/lists/DOMTokenList-coverage-for-attributes.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/nodes/Element-matches.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/dom/nodes/Element-webkitMatchesSelector.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/inserthorizontalrule.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/italic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/multitest.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/removeformat.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/strikethrough.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/subscript.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/superscript.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/editing/run/underline.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/dynamic-append.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/html/semantics/scripting-1/the-script-element/script-charset-01.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/html/semantics/scripting-1/the-script-element/script-charset-02.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/html/webappapis/scripting/events/messageevent-constructor.https.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/pointerevents/extension/idlharness.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-00.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-04.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-12.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-16.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-20.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-24.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-28.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-32.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/selection/addRange-36.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/service-workers/service-worker/fetch-event-within-sw-manual.https.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/web-animations/interfaces/Animatable/animate.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/web-animations/interfaces/KeyframeEffect/constructor.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/web-animations/interfaces/KeyframeEffect/setKeyframes.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webrtc/rtcpeerconnection/setRemoteDescription.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/api/VTTCue/align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/api/VTTCue/lineAlign.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/api/VTTCue/positionAlign.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/api/VTTCue/text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/api/VTTCue/vertical.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/cue-text-parsing/tests/entities.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/cue-text-parsing/tests/tags.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/cue-text-parsing/tests/timestamps.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/cue-text-parsing/tests/tree-building.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/signature-invalid.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-id.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-lines.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-old.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-regionanchor.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-scroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/regions-viewportanchor.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/settings-line.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/settings-position.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/webvtt/parsing/file-parsing/tests/settings-region.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/workers/semantics/encodings/001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] external/wpt/workers/semantics/encodings/002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/basic/001.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/float/float-avoidance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/float/intruding-painted-twice.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/margin-collapse/103.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/positioning/047.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/block/positioning/inline-block-relposition.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusAllStylesAllCorners.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed01.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed02.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed03.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed04.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed05.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDashed06.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDotted01.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/borderRadiusDotted04.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/borders/inline-mask-overlay-image-outset.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/box-shadow/inset-subpixel.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/box-shadow/inset.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css-generated-content/012.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css-generated-content/014.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/acid2-pixel.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/clip-zooming.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/continuationCrash.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/h1-in-section-elements.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/input-search-padding.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/line-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/margin-top-bottom-dynamic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/rem-calc-dynamic-scaling.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/rem-dynamic-scaling.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/css/text-overflow-input.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dnd/link-dragging-draggable-div-with-dragged-link.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dnd/link-dragging-non-draggable-link.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dom/HTMLInputElement/input-image-alt-text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dom/HTMLMeterElement/meter-optimums.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dom/HTMLTableColElement/resize-table-using-col-width.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dynamic/008.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dynamic/012.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/dynamic/positioned-movement-with-positioned-children.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/encoding/denormalised-voiced-japanese-chars.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/events/autoscroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/events/context-no-deselect.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/files/file-in-input-display.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/basic-buttons.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/basic-inputs.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/blankbuttons.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-default-title.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-positioned.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-sizes.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-style-color.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-table-styles.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button-text-transform.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button/button-align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button/button-cannot-be-nested.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button/button-inner-block-reuse.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/button/button-white-space.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-ar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-minimum-date.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-required-ar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-required.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-ru.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-step.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-zoom125.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance-zoom200.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/calendar-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/month-picker-appearance-step.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/month-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/week-picker-appearance-step.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/calendar-picker/week-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/color/color-suggestion-picker-appearance-zoom125.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/color/color-suggestion-picker-appearance-zoom200.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/color/color-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/color/color-suggestion-picker-one-row-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/color/color-suggestion-picker-two-row-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/control-clip-overflow.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/control-clip.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/control-restrict-line-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/datetimelocal/datetimelocal-appearance-l10n.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/file/file-input-direction.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/file/file-input-disabled.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/file/file-input-pressed-state.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/file/input-file-re-render.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/floating-textfield-relayout.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/form-element-geometry.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/formmove3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/image/002.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/image/005.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-appearance-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-button-sizes.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-first-letter.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-type-text-min-width.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/input-value.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/minWidthPercent.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/month/month-appearance-l10n.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/number/number-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/number/number-appearance-spinbutton-disabled-readonly.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/number/number-appearance-spinbutton-layer.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/placeholder-position.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/plaintext-mode-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/search-appearance-basic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/search-cancel-button-style-sharing.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/search-display-none-cancel-button.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/search-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/search-vertical-alignment.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/search/searchfield-heights.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/003.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/004.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/HTMLOptionElement_label01.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/HTMLOptionElement_label02.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/HTMLOptionElement_label03.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/HTMLOptionElement_label04.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/HTMLOptionElement_label05.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/basic-selects.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/disabled-select-change-index.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/hidden-listbox.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-appearance-basic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-appearance-separator.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-bidi-align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-scrollbar-incremental-load.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-width-change.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/listbox-with-display-none-option.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-appearance-none.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-clip.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-deselect-update.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-no-overflow.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-option-wrap.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-restrict-line-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-style-color.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/menulist-width-change.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/optgroup-rendering.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/option-script.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/option-strip-whitespace.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/option-text-clip.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-autofilled.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-background-none.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-baseline.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-block-background.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-change-listbox-size.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-change-listbox-to-popup.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-change-popup-to-listbox.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-dirty-parent-pref-widths.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-disabled-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-initial-position.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-item-background-clip.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-list-box-with-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-listbox-multiple-no-focusring.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-multiple-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-overflow-scroll-inherited.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-overflow-scroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-selected.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-size-invalid.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/select/select-writing-direction-natural.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/stuff-on-my-optgroup.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/submit/submit-appearance-basic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-with-scroll-bar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/tabbing-input-iframe.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/targeted-frame-submission.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text-style-color.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-bkcolor.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-default-bkcolor.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-disabled.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-focus.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-preventDefault.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-readonly.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-selection.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-visibility.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-appearance-width.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-disabled-color.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-double-click-selection-gap-bug.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-field-text-truncated.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-placeholder-paint-order.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-placeholder-visibility-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-placeholder-visibility-3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-readonly-autoscroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-readonly-dimmed.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-spaces.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-table.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-text-double-click.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-text-drag-down.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-text-option-delete.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/input-text-word-wrap.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/text-appearance-basic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/text-font-height-mismatch.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/textfield-outline.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/text/textfield-overflow-by-value-update.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/basic-textareas-quirks.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/basic-textareas.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/placeholder-appearance-textarea.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/reset-textarea.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-appearance-basic.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-placeholder-paint-order.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-placeholder-visibility-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-placeholder-visibility-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-scroll-height.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-scrollbar.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-scrolled-focus-ring.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-scrolled-mask.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-scrolled-type.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/forms/textarea/textarea-setinnerhtml.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/inline/absolute-positioned-inline-in-centred-block.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/invalid/014.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/lists/dynamic-marker-crash.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/lists/ordered-list-with-no-ol-tag.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/multicol/multicol-with-child-renderLayer-for-input.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/overflow/overflow-x-y.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/overflow/scroll-nested-positioned-layer-in-overflow.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/overflow/scrollRevealButton.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/parser/document-write-option.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/parser/entity-comment-in-textarea.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/parser/open-comment-in-textarea.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/replaced/border-radius-clip-content-edge.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/replaced/replaced-breaking-mixture.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/replaced/replaced-breaking.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/replaced/width100percent-button.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/selectors/064.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/selectors/166.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/spatial-navigation/snav-multiple-select-focusring.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/append-cells2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-cell-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-cell.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-column-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-column-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-column-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-column.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-quirks-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-quirks.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-row-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-row-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-row-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table-row.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_border-table.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_layers-hide-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_layers-hide.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-cell-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-cell.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-column-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-column-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-column-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-column.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-row-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-row-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-row-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table-row.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_position-table.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-cell-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-cell.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-column-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-column-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-column-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-column.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-row-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-row-group-collapsed-border.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-row-group.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table-row.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/backgr_simple-table.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/remove-td-display-none.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/spanOverlapRepaint.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/table/text-field-baseline.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/color-emoji.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/complex-preferred-logical-widths.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/drawBidiText.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/emoticons.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/emphasis-complex.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/fallback-traits-fixup.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/font-fallback.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/font-ligature-letter-spacing.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/bidi-listbox-atsui.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/bidi-listbox.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/khmer-selection.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/lang-glyph-cache-separation.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/mixed-directionality-selection.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/international/unicode-bidi-plaintext-in-textarea.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/justify-ideograph-complex.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/justify-ideograph-simple.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/justify-ideograph-vertical.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/midword-break-before-surrogate-pair.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/textIteratorNilRenderer.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/unicode-fallback-font.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/updateNewFont.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/writing-mode/text-combine-various-fonts.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fonts/sans-serif.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] html/details_summary/details-marker-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] html/details_summary/details-replace-summary-child.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] html/details_summary/details-replace-text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/filesystem/input-display.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/inspector-protocol/network-data-length.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/media/media-source/mediasource-is-type-supported.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/misc/acid2-pixel.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/misc/bad-charset-alias.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/streams/readable-streams/count-queuing-strategy-integration.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] http/tests/webfont/popup-menu-load-webfont-after-open.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] ietestcenter/css3/bordersbackgrounds/border-radius-style-001.htm [ Failure ]
+crbug.com/697971 [ Mac10.12 ] ietestcenter/css3/bordersbackgrounds/border-radius-style-002.htm [ Failure ]
+crbug.com/697971 [ Mac10.12 ] images/12-55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] images/182.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] images/2-dht.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] images/23-55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] images/55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] inspector-protocol/layout-fonts/ogham.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] inspector-protocol/layout-fonts/tifinagh.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] media/track/track-cue-rendering-vertical.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] media/video-zoom-controls.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/button-checkbox-click-method-repaint.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/caret-invalidation-in-overflow-scroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/forms/submit-focus-by-mouse-then-keydown.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/resize-iframe-text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/subtree-root-skipped.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/invalidation/textarea-caret.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/selection/text-selection-inline-block-rtl.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/selection/text-selection-inline-block.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] paint/spellmarkers/document-markers-zoom-150.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] plugins/embed-attributes-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] plugins/mouse-click-plugin-clears-selection.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/coords-units-03-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/painting-marker-06-f.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/styling-pres-02-f.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/svgdom-over-01-f.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/text-intro-02-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/text-intro-09-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/types-dom-01-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1-SE/types-dom-05-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1/script-handle-02-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1/script-handle-03-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1/script-handle-04-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1/struct-use-05-b.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/W3C-SVG-1.1/text-fonts-01-t.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/as-border-image/svg-as-border-image-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/as-border-image/svg-as-border-image.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/batik/text/textLayout.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/custom/focus-ring-text.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/custom/inline-svg-in-xhtml.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/hixie/mixed/003.xml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/text/surrogate-pair-queries.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/text/text-path-middle-align.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/text/text-selection-fonts-01-t.svg [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/transforms/svg-css-transforms-clip-path.xhtml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/transforms/svg-css-transforms.xhtml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] svg/zoom/page/zoom-background-images.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug1188.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug1318.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug138725.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug18359.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug194024.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug2479-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug2479-3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug2479-4.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug26178.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug28928.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug29326.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug30559.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug30692.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug33855.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug39209.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug4382.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug4429.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug44505.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug46368-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug46368-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug46480-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug46480-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug51037.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug51727.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug52505.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug52506.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug59354.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug60749.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug68912.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug7342.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug8032-1.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug8858.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/bugs/bug96334.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/collapsing_borders/bug41262-4.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/core/bloomberg.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/core/margins.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/dom/tableDom.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/other/move_row.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/other/test3.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/other/test6.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/other/wa_table_thtd_rowspan.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla/other/wa_table_tr_align.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/bugs/bug1128.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/bugs/bug1725.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/bugs/bug2479-5.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/bugs/bug58402-2.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/bugs/bug91057.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/collapsing_borders/bug41262-5.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/collapsing_borders/bug41262-6.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] transforms/2d/zoom-menulist.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] transforms/svg-vs-css.xhtml [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/button-checkbox-click-method-repaint.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/caret-invalidation-in-overflow-scroll.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/forms/submit-focus-by-mouse-then-keydown.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/resize-iframe-text.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/search-field-cancel.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/subtree-root-skipped.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/disable-spinvalidation/paint/invalidation/textarea-caret.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/12-55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/182.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/2-dht.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/23-55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/55.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/color-profile-background-image-cross-fade.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu-rasterization/images/color-profile-mask-image-svg.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu/fast/canvas/canvas-arc-circumference-fill.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu/fast/canvas/canvas-arc-circumference.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu/fast/canvas/canvas-ellipse-circumference.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/gpu/fast/canvas/canvas-incremental-repaint.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/filesystem/input-display.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/inspector-protocol/network-data-length.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/media/media-source/mediasource-is-type-supported.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/misc/acid2-pixel.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/misc/bad-charset-alias.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/streams/readable-streams/count-queuing-strategy-integration.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/webfont/popup-menu-load-webfont-after-open.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/border-radius-styles-with-composited-child.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/theme-affects-visual-overflow.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/rootlayerscrolls/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/scalefactor200/fast/hidpi/static/popup-menu-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] virtual/scalefactor200/fast/hidpi/static/popup-menu-with-scrollbar-appearance.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] webaudio/dom-exceptions.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] webaudio/unit-tests/audit.html [ Failure ]
+crbug.com/697971 [ Mac10.12 ] fast/text/flexbox-selection-nested.html [ Skip ]
+crbug.com/697971 [ Mac10.12 ] fast/text/flexbox-selection.html [ Skip ]
+crbug.com/697971 [ Mac10.12 ] http/tests/inspector/appcache/appcache-manifest-with-non-existing-file.html [ Timeout ]
+crbug.com/697971 [ Mac10.12 ] virtual/mojo-loading/http/tests/inspector/appcache/appcache-iframe-manifests.html [ Timeout ]
+
 # mojo-loading: This is an experimental feature. failing virtual tests are
 # listed below.
 
@@ -2483,3 +3079,6 @@
 
 # Sheriff failures 2017-03-10
 crbug.com/402805 [ Mac10.10 Mac10.11 ] inspector-protocol/input/emulateTouchFromMouseEvent.html [ Timeout ]
+crbug.com/700374 [ Win ] virtual/mojo-loading/http/tests/inspector/workers-on-navigation.html [ Failure Pass ]
+crbug.com/700374 [ Win ] http/tests/inspector/workers-on-navigation.html [ Failure Pass ]
+crbug.com/700387 [ Win7 ] external/wpt/fullscreen/api/document-exit-fullscreen-nested-in-iframe-manual.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/bindings/record-type.html b/third_party/WebKit/LayoutTests/bindings/record-type.html
new file mode 100644
index 0000000..190c21bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bindings/record-type.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+ /* assert_array_equals() uses same_value() to compare each item, but
+  * Object.entries() returns an array of arrays, so we need to compare
+  * each item with assert_array_equals instead. */
+ function assert_record_equals(actual, expected, description) {
+   for (let i = 0; i < expected.length; ++i) {
+     assert_array_equals(Object.entries(actual)[i], expected[i], description);
+   }
+ }
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   assert_throws(new TypeError(), () => {
+     recordTest.setStringLongRecord(null);
+   }, "Converting null to record should be rejected.");
+   assert_throws(new TypeError(), () => {
+     recordTest.setStringLongRecord(undefined);
+   }, "Converting undefined to record should be rejected.");
+   recordTest.setNullableStringLongRecord(null);
+   assert_equals(recordTest.getNullableStringLongRecord(),
+                 null, "Passing null to a nullable record works");
+   recordTest.setNullableStringLongRecord(undefined);
+   assert_equals(recordTest.getNullableStringLongRecord(),
+                 null, "Passing undefined to a nullable record works");
+ }, "Test handling of null and undefined records");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   recordTest.setStringLongRecord({a: true, false: null, c: "foo"});
+   assert_record_equals(recordTest.getStringLongRecord(),
+                        [['a', 1], ['false', 0], ['c', 0]],
+                        "Types are properly coerced to the record's types");
+ }, "Test type conversion");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   recordTest.setStringLongRecord({false: 1, [false]: 42});
+   assert_record_equals(recordTest.getStringLongRecord(), [['false', 42]],
+                        "Key types are coerced and never repeated");
+ }, "Test duplicate keys");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   recordTest.setStringLongRecord({z: 42, foo: -5, ABC: 0});
+   assert_record_equals(recordTest.getStringLongRecord(),
+                        [['z', 42], ['foo', -5], ['ABC', 0]],
+                        "Keys are not sorted");
+ }, "Test mapping order");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   assert_throws(new TypeError(), () => {
+     recordTest.setByteStringByteStringRecord({'a': 'bc', '\uFFFF': 'foo'});
+   }, "Invalid ByteString key must throw a TypeError");
+   assert_throws(new TypeError(), () => {
+     recordTest.setByteStringByteStringRecord({'xy': 'z', 'foo': '\uFFFF'});
+   }, "Invalid ByteString value must throw a TypeError");
+ }, "Test ByteString validation");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   recordTest.setStringLongRecord({'foo': 0});
+   let record = recordTest.getStringLongRecord();
+   assert_record_equals(record, [['foo', 0]]);
+
+   record.baz = 'quux';
+   assert_equals(Object.keys(recordTest.getStringLongRecord()).length, 1,
+                 "Changing a returned record does not change the record");
+   assert_record_equals(recordTest.getStringLongRecord(), [['foo', 0]],
+                        "Changing a returned record does not change the record");
+
+   assert_record_equals(recordTest.getStringLongRecord(),
+                        Object.entries(recordTest.getStringLongRecord()),
+                        "Record getters always return the same elements");
+   assert_not_equals(recordTest.getStringLongRecord(), recordTest.getStringLongRecord(),
+                     "Record getters always return a new copy");
+ }, "Test records are passed by value");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   assert_false(recordTest.unionReceivedARecord(true),
+                "Passing 'true' should convert the union to boolean");
+   assert_false(recordTest.unionReceivedARecord(false),
+                "Passing 'false' should convert the union to boolean");
+   assert_false(recordTest.unionReceivedARecord(42),
+                "Passing a number should convert the union to boolean");
+   assert_true(recordTest.unionReceivedARecord([1, 2, 3]),
+               "Passing an array should convert the union to a record");
+   assert_true(recordTest.unionReceivedARecord({}),
+               "Passing an object should conver the union to a record");
+ }, "Test unions resolve records");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   let elem = document.createElement('p');
+   recordTest.setStringElementRecord({'elem': elem});
+   assert_equals(recordTest.getStringElementRecord().elem, elem,
+                 "The same DOM object was stored in the record");
+   assert_not_equals(recordTest.getStringElementRecord().elem,
+                     document.createElement('p'),
+                     "The same DOM object was stored in the record");
+
+   elem = document.createElement('br');
+   assert_not_equals(recordTest.getStringElementRecord().elem, elem,
+                     "Changing the original value does not change the record value");
+ }, "Test DOM object types");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+   let getterAlias = recordTest.getUSVStringUSVStringBooleanRecordRecord;
+
+   recordTest.setUSVStringUSVStringBooleanRecordRecord({'foo': {'bar': true}, 'quux': {'baz': false}});
+   let records = recordTest.getUSVStringUSVStringBooleanRecordRecord();
+   assert_array_equals(Object.keys(records), ['foo', 'quux'],
+                       "Nested record types have the correct keys");
+   assert_record_equals(records.foo, [['bar', true]]);
+   assert_record_equals(records.quux, [['baz', false]]);
+ }, "Test nested record types");
+
+ test(() => {
+   let recordTest = internals.recordTest();
+
+   let record = recordTest.returnStringByteStringSequenceRecord();
+   assert_array_equals(Object.keys(record), ['foo', 'bar']);
+   assert_array_equals(record.foo, ['hello, world', 'hi, mom']);
+   assert_array_equals(record.bar, ['goodbye, mom']);
+
+   let other_record = recordTest.returnStringByteStringSequenceRecord();
+   assert_not_equals(record, other_record, "A new object is returned each time");
+ }, "Test sequences in records");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/background_fetch/getTags.html b/third_party/WebKit/LayoutTests/http/tests/background_fetch/getTags.html
new file mode 100644
index 0000000..b6d1e9c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/background_fetch/getTags.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Background Fetch: BackgroundFetchManager.getTags() behaviour.</title>
+    <script src="../resources/testharness.js"></script>
+    <script src="../resources/testharnessreport.js"></script>
+    <script src="../serviceworker/resources/test-helpers.js"></script>
+  </head>
+  <body>
+    <script>
+      // TODO(peter): Remove this test when the IDL harness test has landed as
+      // part of web platform tests.
+      test(function() {
+         assert_own_property(self, 'BackgroundFetchManager', 'BackgroundFetchManager needs to be exposed as a global.');
+         // TODO(peter): Test for the `fetch` method.
+         // TODO(peter): Test for the `get` method.
+
+         assert_own_property(BackgroundFetchManager.prototype, 'getTags');
+
+      }, 'BackgroundFetchManager should be exposed and have the expected interface in a Document.');
+
+      // TODO(peter): Move this to a WPT test when the getTags() function is able
+      // to reflect in-progress background fetches.
+      promise_test(async function(test) {
+        const workerUrl = 'resources/empty-worker.js';
+        const scope = 'resources/scope/' + location.pathname;
+
+        const registration = await service_worker_unregister_and_register(test, workerUrl, scope);
+        await wait_for_state(test, registration.installing, 'activated');
+
+        try {
+          await registration.backgroundFetch.getTags();
+          assert_unreached('getTags() is expected to throw.');
+        } catch (exception) {
+          assert_equals(exception.name, 'NotSupportedError');
+          assert_equals(exception.message, 'Not implemented yet.');
+        }
+      }, 'Change-detector test for the getTags() function.');
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/background_fetch/resources/empty-worker.js b/third_party/WebKit/LayoutTests/http/tests/background_fetch/resources/empty-worker.js
new file mode 100644
index 0000000..49ceb264
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/background_fetch/resources/empty-worker.js
@@ -0,0 +1 @@
+// Do nothing.
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
index 46f223b8..bd27af16 100644
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
@@ -8,6 +8,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
@@ -31,6 +32,7 @@
  method respondWith
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter onupdatefound
  getter paymentAppManager
@@ -60,6 +62,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index a8a5d7bf..376463a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1,3 +1,6 @@
+interface BackgroundFetchManager
+    method constructor
+    method getTags
 interface BarcodeDetector
     method constructor
     method detect
@@ -820,6 +823,7 @@
     method constructor
 interface ServiceWorkerRegistration : EventTarget
     getter active
+    getter backgroundFetch
     getter installing
     getter onupdatefound
     getter paymentAppManager
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
index 46f223b8..bd27af16 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
@@ -8,6 +8,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
@@ -31,6 +32,7 @@
  method respondWith
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter onupdatefound
  getter paymentAppManager
@@ -60,6 +62,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
index d1ddd9d0..e8ca128e 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/navigation-preload-origin-trial-interfaces-expected.txt
@@ -8,6 +8,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
@@ -38,6 +39,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
@@ -68,6 +70,7 @@
  method setHeaderValue
 interface ServiceWorkerRegistration
  getter active
+ getter backgroundFetch
  getter installing
  getter navigationPreload
  getter onupdatefound
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index ef85d30..b2fe4c88 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1,3 +1,6 @@
+interface BackgroundFetchManager
+    method constructor
+    method getTags
 interface BarcodeDetector
     method constructor
     method detect
@@ -827,6 +830,7 @@
     method constructor
 interface ServiceWorkerRegistration : EventTarget
     getter active
+    getter backgroundFetch
     getter installing
     getter navigationPreload
     getter onupdatefound
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 9660576c..3a405602 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -5,6 +5,10 @@
 
 Starting worker: resources/global-interface-listing.js
 [Worker] [INTERFACES]
+[Worker] interface BackgroundFetchManager
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method getTags
 [Worker] interface BarcodeDetector
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
@@ -833,6 +837,7 @@
 [Worker] interface ServiceWorkerRegistration : EventTarget
 [Worker]     attribute @@toStringTag
 [Worker]     getter active
+[Worker]     getter backgroundFetch
 [Worker]     getter installing
 [Worker]     getter onupdatefound
 [Worker]     getter paymentAppManager
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 43318d6..4c9b8b2 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -258,6 +258,10 @@
     getter credential
     getter signature
     method constructor
+interface BackgroundFetchManager
+    attribute @@toStringTag
+    method constructor
+    method getTags
 interface BarProp
     attribute @@toStringTag
     getter visible
@@ -6395,6 +6399,7 @@
 interface ServiceWorkerRegistration : EventTarget
     attribute @@toStringTag
     getter active
+    getter backgroundFetch
     getter installing
     getter onupdatefound
     getter paymentAppManager
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
index 9d8c96f3..9d81b6c 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -5,6 +5,10 @@
 
 Starting worker: resources/global-interface-listing.js
 [Worker] [INTERFACES]
+[Worker] interface BackgroundFetchManager
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method getTags
 [Worker] interface BarcodeDetector
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
@@ -828,6 +832,7 @@
 [Worker] interface ServiceWorkerRegistration : EventTarget
 [Worker]     attribute @@toStringTag
 [Worker]     getter active
+[Worker]     getter backgroundFetch
 [Worker]     getter installing
 [Worker]     getter onupdatefound
 [Worker]     getter paymentAppManager
diff --git a/third_party/WebKit/Source/bindings/bindings.gni b/third_party/WebKit/Source/bindings/bindings.gni
index 19b58f8..057f211 100644
--- a/third_party/WebKit/Source/bindings/bindings.gni
+++ b/third_party/WebKit/Source/bindings/bindings.gni
@@ -233,6 +233,7 @@
     get_path_info([
                     "core/v8/DocumentWriteEvaluatorTest.cpp",
                     "core/v8/IDLTypesTest.cpp",
+                    "core/v8/NativeValueTraitsImplTest.cpp",
                     "core/v8/NativeValueTraitsTest.cpp",
                     "core/v8/ScriptPromisePropertyTest.cpp",
                     "core/v8/ScriptPromiseResolverTest.cpp",
diff --git a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
index f40694d3..3219d73 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
+++ b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
@@ -174,6 +174,8 @@
   "$bindings_core_v8_output_dir/ArrayBufferOrArrayBufferViewOrBlobOrDocumentOrStringOrFormDataOrURLSearchParams.h",
   "$bindings_core_v8_output_dir/ArrayBufferOrArrayBufferViewOrBlobOrUSVString.cpp",
   "$bindings_core_v8_output_dir/ArrayBufferOrArrayBufferViewOrBlobOrUSVString.h",
+  "$bindings_core_v8_output_dir/BooleanOrByteStringByteStringRecord.cpp",
+  "$bindings_core_v8_output_dir/BooleanOrByteStringByteStringRecord.h",
   "$bindings_core_v8_output_dir/CSSStyleValueOrCSSStyleValueSequence.cpp",
   "$bindings_core_v8_output_dir/CSSStyleValueOrCSSStyleValueSequence.h",
   "$bindings_core_v8_output_dir/CSSStyleValueOrCSSStyleValueSequenceOrString.cpp",
@@ -196,6 +198,8 @@
   "$bindings_core_v8_output_dir/EventListenerOptionsOrBoolean.h",
   "$bindings_core_v8_output_dir/FileOrUSVString.cpp",
   "$bindings_core_v8_output_dir/FileOrUSVString.h",
+  "$bindings_core_v8_output_dir/FloatOrStringElementRecord.cpp",
+  "$bindings_core_v8_output_dir/FloatOrStringElementRecord.h",
   "$bindings_core_v8_output_dir/HTMLElementOrLong.cpp",
   "$bindings_core_v8_output_dir/HTMLElementOrLong.h",
   "$bindings_core_v8_output_dir/HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.cpp",
diff --git a/third_party/WebKit/Source/bindings/core/v8/IDLTypes.h b/third_party/WebKit/Source/bindings/core/v8/IDLTypes.h
index ac9546e..1dfff49c 100644
--- a/third_party/WebKit/Source/bindings/core/v8/IDLTypes.h
+++ b/third_party/WebKit/Source/bindings/core/v8/IDLTypes.h
@@ -54,7 +54,7 @@
 struct IDLSequence final : public IDLBase {
  private:
   using CppType = typename NativeValueTraits<T>::ImplType;
-  using MaybeWrappedCppType =
+  using MaybeMemberCppType =
       typename std::conditional<WTF::IsGarbageCollectedType<CppType>::value,
                                 Member<CppType>,
                                 CppType>::type;
@@ -67,12 +67,42 @@
   //    contain Oilpan members. They both also happen to specialize V8TypeOf,
   //    which we use to recognize them.
   using ImplType = typename std::conditional<
-      std::is_same<MaybeWrappedCppType, Member<CppType>>::value ||
+      std::is_same<MaybeMemberCppType, Member<CppType>>::value ||
           std::is_class<typename V8TypeOf<CppType>::Type>::value,
-      HeapVector<MaybeWrappedCppType>,
+      HeapVector<MaybeMemberCppType>,
       Vector<CppType>>::type;
 };
 
+// Record
+template <typename Key, typename Value>
+struct IDLRecord final : public IDLBase {
+  static_assert(std::is_same<Key, IDLByteString>::value ||
+                    std::is_same<Key, IDLString>::value ||
+                    std::is_same<Key, IDLUSVString>::value,
+                "IDLRecord keys must be of a WebIDL string type");
+
+ private:
+  // Record keys are always strings, so we do not need to introspect them.
+  using ValueCppType = typename NativeValueTraits<Value>::ImplType;
+  using MaybeMemberValueCppType = typename std::conditional<
+      WTF::IsGarbageCollectedType<ValueCppType>::value,
+      Member<ValueCppType>,
+      ValueCppType>::type;
+
+ public:
+  // Two kinds of types need HeapVector:
+  // 1. Oilpan types, which are wrapped by Member<>.
+  // 2. IDL unions and dictionaries, which are not Oilpan types themselves (and
+  //    are thus not wrapped by Member<>) but have a trace() method and can
+  //    contain Oilpan members. They both also happen to specialize V8TypeOf,
+  //    which we use to recognize them.
+  using ImplType = typename std::conditional<
+      std::is_same<MaybeMemberValueCppType, Member<ValueCppType>>::value ||
+          std::is_class<typename V8TypeOf<ValueCppType>::Type>::value,
+      HeapVector<std::pair<String, MaybeMemberValueCppType>>,
+      Vector<std::pair<String, ValueCppType>>>::type;
+};
+
 }  // namespace blink
 
 #endif  // IDLTypes_h
diff --git a/third_party/WebKit/Source/bindings/core/v8/IDLTypesTest.cpp b/third_party/WebKit/Source/bindings/core/v8/IDLTypesTest.cpp
index 7115652f..a2ef41302 100644
--- a/third_party/WebKit/Source/bindings/core/v8/IDLTypesTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/IDLTypesTest.cpp
@@ -117,6 +117,28 @@
                  HeapVector<DictionarySequenceOrDictionary>>::value,
     "IDLSequence<union type> produces a HeapVector");
 
+static_assert(std::is_base_of<IDLBase, IDLRecord<IDLString, IDLShort>>::value,
+              "IDLRecord inherits from IDLBase");
+static_assert(std::is_base_of<IDLBase, IDLRecord<IDLString, Element>>::value,
+              "IDLRecord inherits from IDLBase");
+static_assert(std::is_same<IDLRecord<IDLByteString, IDLLong>::ImplType,
+                           Vector<std::pair<String, int32_t>>>::value,
+              "IDLRecord<IDLByteString, IDLLong> produces a Vector");
+static_assert(
+    std::is_same<IDLRecord<IDLByteString, Element>::ImplType,
+                 HeapVector<std::pair<String, Member<Element>>>>::value,
+    "IDLRecord<IDLByteString, GC-type>> produces a HeapVector with Member<>");
+static_assert(
+    std::is_same<IDLRecord<IDLUSVString, InternalDictionary>::ImplType,
+                 HeapVector<std::pair<String, InternalDictionary>>>::value,
+    "IDLRecord<IDLUSVString, dictionary type> produces a HeapVector with no "
+    "Member<>");
+static_assert(
+    std::is_same<
+        IDLRecord<IDLString, DictionarySequenceOrDictionary>::ImplType,
+        HeapVector<std::pair<String, DictionarySequenceOrDictionary>>>::value,
+    "IDLRecord<IDLString, union type> produces a HeapVector with no Member<>");
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h
index 7dfb3fb..3fc5b6d6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h
+++ b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h
@@ -17,9 +17,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLBoolean>
     : public NativeValueTraitsBase<IDLBoolean> {
-  static inline bool nativeValue(v8::Isolate* isolate,
-                                 v8::Local<v8::Value> value,
-                                 ExceptionState& exceptionState) {
+  static bool nativeValue(v8::Isolate* isolate,
+                          v8::Local<v8::Value> value,
+                          ExceptionState& exceptionState) {
     return toBoolean(isolate, value, exceptionState);
   }
 };
@@ -33,17 +33,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLByte>
     : public NativeValueTraitsBase<IDLByte> {
-  static inline int8_t nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static int8_t nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline int8_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static int8_t nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState,
+                            IntegerConversionConfiguration conversionMode) {
     return toInt8(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -51,17 +50,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLOctet>
     : public NativeValueTraitsBase<IDLOctet> {
-  static inline uint8_t nativeValue(v8::Isolate* isolate,
-                                    v8::Local<v8::Value> value,
-                                    ExceptionState& exceptionState) {
+  static uint8_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline uint8_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static uint8_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState,
+                             IntegerConversionConfiguration conversionMode) {
     return toUInt8(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -69,17 +67,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLShort>
     : public NativeValueTraitsBase<IDLShort> {
-  static inline int16_t nativeValue(v8::Isolate* isolate,
-                                    v8::Local<v8::Value> value,
-                                    ExceptionState& exceptionState) {
+  static int16_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline int16_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static int16_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState,
+                             IntegerConversionConfiguration conversionMode) {
     return toInt16(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -87,17 +84,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUnsignedShort>
     : public NativeValueTraitsBase<IDLUnsignedShort> {
-  static inline uint16_t nativeValue(v8::Isolate* isolate,
-                                     v8::Local<v8::Value> value,
-                                     ExceptionState& exceptionState) {
+  static uint16_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline uint16_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static uint16_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState,
+                              IntegerConversionConfiguration conversionMode) {
     return toUInt16(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -105,17 +101,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLLong>
     : public NativeValueTraitsBase<IDLLong> {
-  static inline int32_t nativeValue(v8::Isolate* isolate,
-                                    v8::Local<v8::Value> value,
-                                    ExceptionState& exceptionState) {
+  static int32_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline int32_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static int32_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState,
+                             IntegerConversionConfiguration conversionMode) {
     return toInt32(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -123,17 +118,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUnsignedLong>
     : public NativeValueTraitsBase<IDLUnsignedLong> {
-  static inline uint32_t nativeValue(v8::Isolate* isolate,
-                                     v8::Local<v8::Value> value,
-                                     ExceptionState& exceptionState) {
+  static uint32_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline uint32_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static uint32_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState,
+                              IntegerConversionConfiguration conversionMode) {
     return toUInt32(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -141,17 +135,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLLongLong>
     : public NativeValueTraitsBase<IDLLongLong> {
-  static inline int64_t nativeValue(v8::Isolate* isolate,
-                                    v8::Local<v8::Value> value,
-                                    ExceptionState& exceptionState) {
+  static int64_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline int64_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static int64_t nativeValue(v8::Isolate* isolate,
+                             v8::Local<v8::Value> value,
+                             ExceptionState& exceptionState,
+                             IntegerConversionConfiguration conversionMode) {
     return toInt64(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -159,17 +152,16 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLong>
     : public NativeValueTraitsBase<IDLUnsignedLongLong> {
-  static inline uint64_t nativeValue(v8::Isolate* isolate,
-                                     v8::Local<v8::Value> value,
-                                     ExceptionState& exceptionState) {
+  static uint64_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, NormalConversion);
   }
 
-  static inline uint64_t nativeValue(
-      v8::Isolate* isolate,
-      v8::Local<v8::Value> value,
-      ExceptionState& exceptionState,
-      IntegerConversionConfiguration conversionMode) {
+  static uint64_t nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState,
+                              IntegerConversionConfiguration conversionMode) {
     return toUInt64(isolate, value, conversionMode, exceptionState);
   }
 };
@@ -178,9 +170,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLByteString>
     : public NativeValueTraitsBase<IDLByteString> {
-  static inline String nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static String nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return toByteString(isolate, value, exceptionState);
   }
 };
@@ -188,17 +180,17 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLString>
     : public NativeValueTraitsBase<IDLString> {
-  static inline String nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static String nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return nativeValue<V8StringResourceMode::DefaultMode>(isolate, value,
                                                           exceptionState);
   }
 
   template <V8StringResourceMode Mode = DefaultMode>
-  static inline String nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static String nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     V8StringResource<Mode> string(value);
     if (!string.prepare(isolate, exceptionState))
       return String();
@@ -209,9 +201,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUSVString>
     : public NativeValueTraitsBase<IDLUSVString> {
-  static inline String nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static String nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return toUSVString(isolate, value, exceptionState);
   }
 };
@@ -220,9 +212,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLDouble>
     : public NativeValueTraitsBase<IDLDouble> {
-  static inline double nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static double nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return toRestrictedDouble(isolate, value, exceptionState);
   }
 };
@@ -230,9 +222,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUnrestrictedDouble>
     : public NativeValueTraitsBase<IDLUnrestrictedDouble> {
-  static inline double nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static double nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return toDouble(isolate, value, exceptionState);
   }
 };
@@ -240,9 +232,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLFloat>
     : public NativeValueTraitsBase<IDLFloat> {
-  static inline float nativeValue(v8::Isolate* isolate,
-                                  v8::Local<v8::Value> value,
-                                  ExceptionState& exceptionState) {
+  static float nativeValue(v8::Isolate* isolate,
+                           v8::Local<v8::Value> value,
+                           ExceptionState& exceptionState) {
     return toRestrictedFloat(isolate, value, exceptionState);
   }
 };
@@ -250,9 +242,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLUnrestrictedFloat>
     : public NativeValueTraitsBase<IDLUnrestrictedFloat> {
-  static inline float nativeValue(v8::Isolate* isolate,
-                                  v8::Local<v8::Value> value,
-                                  ExceptionState& exceptionState) {
+  static float nativeValue(v8::Isolate* isolate,
+                           v8::Local<v8::Value> value,
+                           ExceptionState& exceptionState) {
     return toFloat(isolate, value, exceptionState);
   }
 };
@@ -261,14 +253,14 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLPromise>
     : public NativeValueTraitsBase<IDLPromise> {
-  static inline ScriptPromise nativeValue(v8::Isolate* isolate,
-                                          v8::Local<v8::Value> value,
-                                          ExceptionState& exceptionState) {
+  static ScriptPromise nativeValue(v8::Isolate* isolate,
+                                   v8::Local<v8::Value> value,
+                                   ExceptionState& exceptionState) {
     return nativeValue(isolate, value);
   }
 
-  static inline ScriptPromise nativeValue(v8::Isolate* isolate,
-                                          v8::Local<v8::Value> value) {
+  static ScriptPromise nativeValue(v8::Isolate* isolate,
+                                   v8::Local<v8::Value> value) {
     return ScriptPromise::cast(ScriptState::current(isolate), value);
   }
 };
@@ -277,9 +269,9 @@
 template <>
 struct CORE_EXPORT NativeValueTraits<IDLDate>
     : public NativeValueTraitsBase<IDLDate> {
-  static inline double nativeValue(v8::Isolate* isolate,
-                                   v8::Local<v8::Value> value,
-                                   ExceptionState& exceptionState) {
+  static double nativeValue(v8::Isolate* isolate,
+                            v8::Local<v8::Value> value,
+                            ExceptionState& exceptionState) {
     return toCoreDate(isolate, value, exceptionState);
   }
 };
@@ -291,20 +283,145 @@
   // Nondependent types need to be explicitly qualified to be accessible.
   using typename NativeValueTraitsBase<IDLSequence<T>>::ImplType;
 
-  static inline ImplType nativeValue(v8::Isolate* isolate,
-                                     v8::Local<v8::Value> value,
-                                     ExceptionState& exceptionState) {
+  static ImplType nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState) {
     return nativeValue(isolate, value, exceptionState, 0);
   }
 
-  static inline ImplType nativeValue(v8::Isolate* isolate,
-                                     v8::Local<v8::Value> value,
-                                     ExceptionState& exceptionState,
-                                     int index) {
+  static ImplType nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> value,
+                              ExceptionState& exceptionState,
+                              int index) {
     return toImplArray<ImplType, T>(value, index, isolate, exceptionState);
   }
 };
 
+// Records
+template <typename K, typename V>
+struct NativeValueTraits<IDLRecord<K, V>>
+    : public NativeValueTraitsBase<IDLRecord<K, V>> {
+  // Nondependent types need to be explicitly qualified to be accessible.
+  using typename NativeValueTraitsBase<IDLRecord<K, V>>::ImplType;
+
+  // Converts a JavaScript value |O| to an IDL record<K, V> value. In C++, a
+  // record is represented as a Vector<std::pair<k, v>> (or a HeapVector if
+  // necessary). See https://heycam.github.io/webidl/#es-record.
+  static ImplType nativeValue(v8::Isolate* isolate,
+                              v8::Local<v8::Value> v8Value,
+                              ExceptionState& exceptionState) {
+    v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+    // "1. If Type(O) is not Object, throw a TypeError."
+    if (!v8Value->IsObject()) {
+      exceptionState.throwTypeError(
+          "Only objects can be converted to record<K,V> types");
+      return ImplType();
+    }
+    v8::Local<v8::Object> v8Object = v8::Local<v8::Object>::Cast(v8Value);
+    v8::TryCatch block(isolate);
+
+    // "3. Let keys be ? O.[[OwnPropertyKeys]]()."
+    v8::Local<v8::Array> keys;
+    // While we could pass v8::ONLY_ENUMERABLE below, doing so breaks
+    // web-platform-tests' headers-record.html and deviates from the spec
+    // algorithm.
+    // Symbols are being skipped due to
+    // https://github.com/heycam/webidl/issues/294.
+    if (!v8Object
+             ->GetOwnPropertyNames(context,
+                                   static_cast<v8::PropertyFilter>(
+                                       v8::PropertyFilter::ALL_PROPERTIES |
+                                       v8::PropertyFilter::SKIP_SYMBOLS))
+             .ToLocal(&keys)) {
+      exceptionState.rethrowV8Exception(block.Exception());
+      return ImplType();
+    }
+    if (keys->Length() > ImplType::maxCapacity()) {
+      exceptionState.throwRangeError("Array length exceeds supported limit.");
+      return ImplType();
+    }
+
+    // "2. Let result be a new empty instance of record<K, V>."
+    ImplType result;
+    result.reserveInitialCapacity(keys->Length());
+
+    // The conversion algorithm needs a data structure with fast insertion at
+    // the end while at the same time requiring fast checks for previous insert
+    // of a given key. |seenKeys| is a key/position in |result| map that aids in
+    // the latter part.
+    HashMap<String, size_t> seenKeys;
+
+    for (uint32_t i = 0; i < keys->Length(); ++i) {
+      // "4. Repeat, for each element key of keys in List order:"
+      v8::Local<v8::Value> key;
+      if (!keys->Get(context, i).ToLocal(&key)) {
+        exceptionState.rethrowV8Exception(block.Exception());
+        return ImplType();
+      }
+
+      // "4.1. Let desc be ? O.[[GetOwnProperty]](key)."
+      v8::Local<v8::Value> desc;
+      if (!v8Object
+               ->GetOwnPropertyDescriptor(
+                   context, key->ToString(context).ToLocalChecked())
+               .ToLocal(&desc)) {
+        exceptionState.rethrowV8Exception(block.Exception());
+        return ImplType();
+      }
+
+      // "4.2. If desc is not undefined and desc.[[Enumerable]] is true:"
+      // We can call ToLocalChecked() and ToChecked() here because
+      // GetOwnPropertyDescriptor is responsible for catching any exceptions
+      // and failures, and if we got to this point of the code we have a proper
+      // object that was not created by a user.
+      DCHECK(desc->IsObject());
+      v8::Local<v8::Value> enumerable =
+          v8::Local<v8::Object>::Cast(desc)
+              ->Get(context, v8String(isolate, "enumerable"))
+              .ToLocalChecked();
+      if (!enumerable->BooleanValue(context).ToChecked())
+        continue;
+
+      // "4.2.1. Let typedKey be key converted to an IDL value of type K."
+      String typedKey =
+          NativeValueTraits<K>::nativeValue(isolate, key, exceptionState);
+      if (exceptionState.hadException())
+        return ImplType();
+
+      // "4.2.2. Let value be ? Get(O, key)."
+      v8::Local<v8::Value> value;
+      if (!v8Object->Get(context, key).ToLocal(&value)) {
+        exceptionState.rethrowV8Exception(block.Exception());
+        return ImplType();
+      }
+
+      // "4.2.3. Let typedValue be value converted to an IDL value of type V."
+      typename ImplType::ValueType::second_type typedValue =
+          NativeValueTraits<V>::nativeValue(isolate, value, exceptionState);
+      if (exceptionState.hadException())
+        return ImplType();
+
+      if (seenKeys.contains(typedKey)) {
+        // "4.2.4. If typedKey is already a key in result, set its value to
+        //         typedValue.
+        //         Note: This can happen when O is a proxy object."
+        const size_t pos = seenKeys.at(typedKey);
+        result[pos] = std::make_pair(typedKey, typedValue);
+      } else {
+        // "4.2.5. Otherwise, append to result a mapping (typedKey,
+        // typedValue)."
+        // Note we can take this shortcut because we are always appending.
+        const size_t pos = result.size();
+        seenKeys.set(typedKey, pos);
+        result.uncheckedAppend(std::make_pair(typedKey, typedValue));
+      }
+    }
+    // "5. Return result."
+    return result;
+  }
+};
+
 }  // namespace blink
 
 #endif  // NativeValueTraitsImpl_h
diff --git a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
new file mode 100644
index 0000000..5c3b2e4a7
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
@@ -0,0 +1,245 @@
+// 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 "bindings/core/v8/NativeValueTraitsImpl.h"
+
+#include <utility>
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/ToV8.h"
+#include "bindings/core/v8/V8BindingForTesting.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/Vector.h"
+
+namespace blink {
+
+namespace {
+
+template <typename T>
+v8::Local<v8::Value> ToV8(V8TestingScope* scope, T value) {
+  return blink::ToV8(value, scope->context()->Global(), scope->isolate());
+}
+
+void ThrowException(v8::Local<v8::Name>,
+                    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  info.GetIsolate()->ThrowException(v8String(info.GetIsolate(), "bogus!"));
+}
+
+void ReturnBogusObjectDescriptor(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  auto context = isolate->GetCurrentContext();
+
+  v8::Local<v8::Object> descriptor = v8::Object::New(isolate);
+  EXPECT_TRUE(
+      v8CallBoolean(descriptor->Set(context, v8String(isolate, "configurable"),
+                                    v8::Boolean::New(isolate, true))));
+  EXPECT_TRUE(v8CallBoolean(descriptor->SetAccessor(
+      context, v8String(isolate, "enumerable"), ThrowException)));
+  info.GetReturnValue().Set(descriptor);
+}
+
+TEST(NativeValueTraitsImplTest, IDLRecord) {
+  V8TestingScope scope;
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLString, IDLOctet>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_TRUE(record.isEmpty());
+  }
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), ToV8(&scope, "bar"), ToV8(&scope, -1024))));
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 42))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLByteString, IDLLong>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(2U, record.size());
+    EXPECT_EQ(std::make_pair(String("foo"), int32_t(42)), record[0]);
+    EXPECT_EQ(std::make_pair(String("bar"), int32_t(-1024)), record[1]);
+  }
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(v8Object->DefineOwnProperty(
+        scope.context(), v8String(scope.isolate(), "bar"), ToV8(&scope, -1024),
+        v8::PropertyAttribute::DontEnum)));
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "baz"), ToV8(&scope, 42))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLByteString, IDLLong>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(2U, record.size());
+    EXPECT_EQ(std::make_pair(String("foo"), int32_t(34)), record[0]);
+    EXPECT_EQ(std::make_pair(String("baz"), int32_t(42)), record[1]);
+  }
+  {
+    // Exceptions are being thrown in this test, so we need another scope.
+    V8TestingScope scope;
+    v8::Local<v8::Object> originalObject = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(originalObject->Set(
+        scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(originalObject->Set(
+        scope.context(), ToV8(&scope, "bar"), ToV8(&scope, 42))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLString, IDLLong>>::nativeValue(
+            scope.isolate(), originalObject, exceptionState);
+    EXPECT_EQ(2U, record.size());
+
+    v8::Local<v8::Object> handler = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(handler->Set(
+        scope.context(), ToV8(&scope, "getOwnPropertyDescriptor"),
+        v8::Function::New(scope.context(), ReturnBogusObjectDescriptor)
+            .ToLocalChecked())));
+    v8::Local<v8::Proxy> proxy =
+        v8::Proxy::New(scope.context(), originalObject, handler)
+            .ToLocalChecked();
+    ExceptionState exceptionStateFromProxy(
+        scope.isolate(), ExceptionState::ExecutionContext,
+        "NativeValueTraitsImplTest", "IDLRecordTest");
+    const auto& recordFromProxy =
+        NativeValueTraits<IDLRecord<IDLString, IDLLong>>::nativeValue(
+            scope.isolate(), proxy, exceptionStateFromProxy);
+    EXPECT_EQ(0U, recordFromProxy.size());
+    EXPECT_TRUE(exceptionStateFromProxy.hadException());
+    EXPECT_TRUE(exceptionStateFromProxy.message().isEmpty());
+    v8::Local<v8::Value> v8Exception = exceptionStateFromProxy.getException();
+    EXPECT_TRUE(v8Exception->IsString());
+    EXPECT_TRUE(
+        v8String(scope.isolate(), "bogus!")
+            ->Equals(v8Exception->ToString(scope.context()).ToLocalChecked()));
+  }
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 42))));
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "bar"), ToV8(&scope, 0))));
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(scope.context(), ToV8(&scope, "xx"),
+                                            ToV8(&scope, true))));
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), ToV8(&scope, "abcd"), ToV8(&scope, false))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLByteString, IDLBoolean>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(4U, record.size());
+    EXPECT_EQ(std::make_pair(String("foo"), true), record[0]);
+    EXPECT_EQ(std::make_pair(String("bar"), false), record[1]);
+    EXPECT_EQ(std::make_pair(String("xx"), true), record[2]);
+    EXPECT_EQ(std::make_pair(String("abcd"), false), record[3]);
+  }
+  {
+    v8::Local<v8::Array> v8StringArray = v8::Array::New(scope.isolate(), 2);
+    EXPECT_TRUE(v8CallBoolean(v8StringArray->Set(
+        scope.context(), ToV8(&scope, 0), ToV8(&scope, "Hello, World!"))));
+    EXPECT_TRUE(v8CallBoolean(v8StringArray->Set(
+        scope.context(), ToV8(&scope, 1), ToV8(&scope, "Hi, Mom!"))));
+    EXPECT_TRUE(v8CallBoolean(v8StringArray->Set(
+        scope.context(), ToV8(&scope, "foo"), ToV8(&scope, "Ohai"))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLUSVString, IDLString>>::nativeValue(
+            scope.isolate(), v8StringArray, exceptionState);
+    EXPECT_EQ(3U, record.size());
+    EXPECT_EQ(std::make_pair(String("0"), String("Hello, World!")), record[0]);
+    EXPECT_EQ(std::make_pair(String("1"), String("Hi, Mom!")), record[1]);
+    EXPECT_EQ(std::make_pair(String("foo"), String("Ohai")), record[2]);
+  }
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), v8::Symbol::GetToStringTag(scope.isolate()),
+        ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 42))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLString, IDLShort>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(1U, record.size());
+    EXPECT_EQ(std::make_pair(String("foo"), int16_t(42)), record[0]);
+  }
+  {
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), v8::Symbol::GetToStringTag(scope.isolate()),
+        ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(
+        v8Object->Set(scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 42))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLString, IDLShort>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(1U, record.size());
+    EXPECT_EQ(std::make_pair(String("foo"), int16_t(42)), record[0]);
+  }
+  {
+    v8::Local<v8::Object> v8ParentObject = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(v8ParentObject->Set(
+        scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 34))));
+    EXPECT_TRUE(v8CallBoolean(v8ParentObject->Set(
+        scope.context(), ToV8(&scope, "bar"), ToV8(&scope, 512))));
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(
+        v8CallBoolean(v8Object->SetPrototype(scope.context(), v8ParentObject)));
+
+    NonThrowableExceptionState exceptionState;
+    auto record =
+        NativeValueTraits<IDLRecord<IDLString, IDLUnsignedLong>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_TRUE(record.isEmpty());
+
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), ToV8(&scope, "quux"), ToV8(&scope, 42))));
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), ToV8(&scope, "foo"), ToV8(&scope, 1024))));
+    record =
+        NativeValueTraits<IDLRecord<IDLString, IDLUnsignedLong>>::nativeValue(
+            scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(2U, record.size());
+    EXPECT_EQ(std::make_pair(String("quux"), uint32_t(42)), record[0]);
+    EXPECT_EQ(std::make_pair(String("foo"), uint32_t(1024)), record[1]);
+  }
+  {
+    v8::Local<v8::Array> v8StringArray = v8::Array::New(scope.isolate(), 2);
+    EXPECT_TRUE(v8CallBoolean(v8StringArray->Set(
+        scope.context(), ToV8(&scope, 0), ToV8(&scope, "Hello, World!"))));
+    EXPECT_TRUE(v8CallBoolean(v8StringArray->Set(
+        scope.context(), ToV8(&scope, 1), ToV8(&scope, "Hi, Mom!"))));
+    v8::Local<v8::Object> v8Object = v8::Object::New(scope.isolate());
+    EXPECT_TRUE(v8CallBoolean(v8Object->Set(
+        scope.context(), ToV8(&scope, "foo"), ToV8(&scope, v8StringArray))));
+
+    NonThrowableExceptionState exceptionState;
+    const auto& record =
+        NativeValueTraits<IDLRecord<IDLString, IDLSequence<IDLString>>>::
+            nativeValue(scope.isolate(), v8Object, exceptionState);
+    EXPECT_EQ(1U, record.size());
+    EXPECT_EQ("foo", record[0].first);
+    EXPECT_EQ("Hello, World!", record[0].second[0]);
+    EXPECT_EQ("Hi, Mom!", record[0].second[1]);
+  }
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ToV8.h b/third_party/WebKit/Source/bindings/core/v8/ToV8.h
index 0121859..b1982916 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ToV8.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ToV8.h
@@ -231,6 +231,8 @@
   return toV8SequenceInternal(value, creationContext, isolate);
 }
 
+// The following two overloads are also used to convert record<K,V> IDL types
+// back into ECMAScript Objects.
 template <typename T>
 inline v8::Local<v8::Value> ToV8(const Vector<std::pair<String, T>>& value,
                                  v8::Local<v8::Object> creationContext,
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_interface.py b/third_party/WebKit/Source/bindings/scripts/v8_interface.py
index c405fb8..a2fd8a7 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_interface.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_interface.py
@@ -1101,7 +1101,8 @@
             # Array in overloaded method: http://crbug.com/262383
             yield '%s->IsArray()' % cpp_value, method
     for idl_type, method in idl_types_methods:
-        if idl_type.is_dictionary or idl_type.name == 'Dictionary' or idl_type.is_callback_interface:
+        if idl_type.is_dictionary or idl_type.name == 'Dictionary' or \
+           idl_type.is_callback_interface or idl_type.is_record_type:
             # FIXME: should be '{1}->IsObject() && !{1}->IsRegExp()'.format(cpp_value)
             # FIXME: the IsRegExp checks can be skipped if we've
             # already generated tests for them.
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_types.py b/third_party/WebKit/Source/bindings/scripts/v8_types.py
index 536e1d6..304f56b8 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_types.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_types.py
@@ -41,7 +41,12 @@
 
 import posixpath
 
-from idl_types import IdlTypeBase, IdlType, IdlUnionType, IdlArrayOrSequenceType, IdlNullableType
+from idl_types import IdlArrayOrSequenceType
+from idl_types import IdlNullableType
+from idl_types import IdlRecordType
+from idl_types import IdlType
+from idl_types import IdlTypeBase
+from idl_types import IdlUnionType
 import v8_attributes  # for IdlType.constructor_type_name
 from v8_globals import includes
 from v8_utilities import extended_attribute_value_contains
@@ -140,7 +145,7 @@
             bool, True if the C++ type is used as a variadic argument of a method.
         used_in_cpp_sequence:
             bool, True if the C++ type is used as an element of a container.
-            Containers can be an array, a sequence or a dictionary.
+            Containers can be an array, a sequence, a dictionary or a record.
     """
     def string_mode():
         if idl_type.is_nullable:
@@ -166,6 +171,16 @@
             return 'const %s&' % vector_template_type
         return vector_template_type
 
+    # Record types.
+    if idl_type.is_record_type:
+        vector_type = cpp_ptr_type('Vector', 'HeapVector', idl_type.value_type.is_gc_type)
+        value_type = idl_type.value_type.cpp_type_args(used_in_cpp_sequence=True)
+        vector_template_type = cpp_template_type(vector_type,
+                                                 'std::pair<String, %s>' % value_type)
+        if used_as_rvalue_type:
+            return 'const %s&' % vector_template_type
+        return vector_template_type
+
     # Simple types
     base_idl_type = idl_type.base_type
 
@@ -317,6 +332,8 @@
 IdlUnionType.is_traceable = property(lambda self: True)
 IdlArrayOrSequenceType.is_traceable = property(
     lambda self: self.element_type.is_traceable)
+IdlRecordType.is_traceable = property(
+    lambda self: self.value_type.is_traceable)
 
 
 ################################################################################
@@ -403,6 +420,13 @@
 IdlArrayOrSequenceType.includes_for_type = includes_for_array_or_sequence_type
 
 
+def includes_for_record_type(idl_type, extended_attributes=None):
+    return set.union(idl_type.key_type.includes_for_type(extended_attributes),
+                     idl_type.value_type.includes_for_type(extended_attributes))
+
+IdlRecordType.includes_for_type = includes_for_record_type
+
+
 def add_includes_for_type(idl_type, extended_attributes=None):
     includes.update(idl_type.includes_for_type(extended_attributes))
 
@@ -499,6 +523,7 @@
 
 IdlType.v8_conversion_needs_exception_state = property(v8_conversion_needs_exception_state)
 IdlArrayOrSequenceType.v8_conversion_needs_exception_state = True
+IdlRecordType.v8_conversion_needs_exception_state = True
 IdlUnionType.v8_conversion_needs_exception_state = True
 
 
@@ -522,6 +547,24 @@
 IdlType.v8_conversion_is_trivial = property(v8_conversion_is_trivial)
 
 
+def native_value_traits_type_name(idl_type):
+    if idl_type.is_nullable:
+        idl_type = idl_type.inner_type
+
+    if idl_type.native_array_element_type:
+        name = 'IDLSequence<%s>' % native_value_traits_type_name(idl_type.native_array_element_type)
+    elif idl_type.is_record_type:
+        name = 'IDLRecord<%s, %s>' % (native_value_traits_type_name(idl_type.key_type),
+                                      native_value_traits_type_name(idl_type.value_type))
+    elif idl_type.is_basic_type or idl_type.name == 'Promise':
+        name = 'IDL%s' % idl_type.name
+    elif idl_type.implemented_as is not None:
+        name = idl_type.implemented_as
+    else:
+        name = idl_type.name
+    return name
+
+
 def v8_value_to_cpp_value(idl_type, extended_attributes, v8_value, variable_name, index, isolate):
     if idl_type.name == 'void':
         return ''
@@ -569,14 +612,9 @@
     elif idl_type.v8_conversion_needs_exception_state:
         # Effectively, this if branch means everything with v8_conversion_needs_exception_state == True
         # except for unions, sequences and dictionary interfaces.
-        if idl_type.is_nullable:
-            idl_type = idl_type.inner_type
-        if idl_type.is_primitive_type or idl_type.name in ('ByteString', 'Date', 'Promise', 'USVString'):
-            trait_type = 'IDL%s' % idl_type.name
-        else:
-            trait_type = idl_type.name
+        base_idl_type = native_value_traits_type_name(idl_type)
         cpp_expression_format = (
-            'NativeValueTraits<%s>::nativeValue({isolate}, {arguments})' % trait_type)
+            'NativeValueTraits<{idl_type}>::nativeValue({isolate}, {arguments})')
     else:
         cpp_expression_format = (
             'V8{idl_type}::toImplWithTypeCheck({isolate}, {v8_value})')
@@ -748,6 +786,10 @@
     if native_array_element_type:
         return 'FrozenArray' if idl_type.is_frozen_array else 'array'
 
+    # Record types.
+    if idl_type.is_record_type:
+        return 'Record'
+
     # Simple types
     base_idl_type = idl_type.base_type
     # Basic types, without additional includes
@@ -802,6 +844,8 @@
     'EventHandler': 'v8SetReturnValue(info, {cpp_value})',
     'ScriptValue': 'v8SetReturnValue(info, {cpp_value})',
     'SerializedScriptValue': 'v8SetReturnValue(info, {cpp_value})',
+    # Records.
+    'Record': 'v8SetReturnValue(info, ToV8({cpp_value}, info.Holder(), info.GetIsolate()))',
     # DOMWrapper
     'DOMWrapperForMainWorld': 'v8SetReturnValueForMainWorld(info, {cpp_value})',
     'DOMWrapperFast': 'v8SetReturnValueFast(info, {cpp_value}, {script_wrappable})',
@@ -903,6 +947,7 @@
         'V8AbstractEventListener::cast({cpp_value})->getListenerOrNull(' +
         '{isolate}, impl->getExecutionContext()) : ' +
         'v8::Null({isolate}).As<v8::Value>()'),
+    'Record': 'ToV8({cpp_value}, {creation_context}, {isolate})',
     'ScriptValue': '{cpp_value}.v8Value()',
     'SerializedScriptValue': 'v8Deserialize({isolate}, {cpp_value})',
     # General
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_union.py b/third_party/WebKit/Source/bindings/scripts/v8_union.py
index 9131264..bed82625 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_union.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_union.py
@@ -52,6 +52,7 @@
     interface_types = []
     numeric_type = None
     object_type = None
+    record_type = None
     string_type = None
     for member in union_type.member_types:
         context = member_context(member, interfaces_info)
@@ -74,9 +75,13 @@
             array_or_sequence_type = context
         # "Dictionary" is an object, rather than an IDL dictionary.
         elif member.base_type == 'Dictionary':
-            if object_type:
+            if object_type or record_type:
                 raise Exception('%s is ambiguous.' % union_type.name)
             object_type = context
+        elif member.is_record_type:
+            if object_type or record_type:
+                raise Exception('%s is ambiguous.' % union_type.name)
+            record_type = context
         elif member.is_interface_type:
             interface_types.append(context)
         elif member is union_type.boolean_member_type:
@@ -111,6 +116,7 @@
         'members': members,
         'numeric_type': numeric_type,
         'object_type': object_type,
+        'record_type': record_type,
         'string_type': string_type,
         'type_string': str(union_type),
         'v8_class': v8_types.v8_type(cpp_class),
@@ -128,7 +134,12 @@
             cpp_includes.update(member.includes_for_type())
             header_forward_decls.add(member.implemented_as)
     else:
-        cpp_includes.update(member.includes_for_type())
+        if member.is_record_type:
+            # The headers for both T and U must be present when
+            # Vector<std::pair<T, U>> is declared.
+            header_includes.update(member.includes_for_type())
+        else:
+            cpp_includes.update(member.includes_for_type())
 
 
 def member_context(member, interfaces_info):
diff --git a/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
index ac99c69c..ff2eca5 100644
--- a/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
@@ -98,7 +98,7 @@
 
   {% endif %}
   {% if array_or_sequence_type %}
-  {# 12.1, 12.2. Arrays and Sequences #}
+  {# 11.1, 11.2. Arrays and Sequences #}
   {# FIXME: This should also check "object but not RegExp". Add checks
      when we implement conversions for Date and RegExp. #}
   {# TODO(bashi): Should check @@iterator symbol instead. #}
@@ -110,7 +110,7 @@
 
   {% endif %}
   {% if dictionary_type %}
-  {# 12.3. Dictionaries #}
+  {# 11.3. Dictionaries #}
   if (v8Value->IsObject()) {
     {{v8_value_to_local_cpp_value(dictionary_type) | indent}}
     impl.set{{dictionary_type.type_name}}(cppValue);
@@ -118,8 +118,17 @@
   }
 
   {% endif %}
-  {# TODO(bashi): Support 12.4 Callback interface when we need it #}
-  {# 12.5. Objects #}
+  {# 11.4. Records #}
+  {% if record_type %}
+  if (v8Value->IsObject()) {
+    {{v8_value_to_local_cpp_value(record_type) | indent}}
+    impl.set{{record_type.type_name}}(cppValue);
+    return;
+  }
+
+  {% endif %}
+  {# TODO(bashi): Support 11.5 Callback interface when we need it #}
+  {# 11.6. Objects #}
   {% if object_type %}
   if (isUndefinedOrNull(v8Value) || v8Value->IsObject()) {
     {{v8_value_to_local_cpp_value(object_type) | indent}}
@@ -131,7 +140,7 @@
   {# FIXME: In some cases, we can omit boolean and numeric type checks because
      we have fallback conversions. (step 16 and 17) #}
   {% if boolean_type %}
-  {# 13. Boolean #}
+  {# 12. Boolean #}
   if (v8Value->IsBoolean()) {
     impl.setBoolean(v8Value.As<v8::Boolean>()->Value());
     return;
@@ -139,7 +148,7 @@
 
   {% endif %}
   {% if numeric_type %}
-  {# 14. Number #}
+  {# 13. Number #}
   if (v8Value->IsNumber()) {
     {{v8_value_to_local_cpp_value(numeric_type) | indent}}
     impl.set{{numeric_type.type_name}}(cppValue);
@@ -148,7 +157,7 @@
 
   {% endif %}
   {% if string_type %}
-  {# 15. String #}
+  {# 14. String #}
   {
     {{v8_value_to_local_cpp_value(string_type) | indent}}
     {% if string_type.enum_values %}
@@ -160,7 +169,7 @@
     return;
   }
 
-  {# 16. Number (fallback) #}
+  {# 15. Number (fallback) #}
   {% elif numeric_type %}
   {
     {{v8_value_to_local_cpp_value(numeric_type) | indent}}
@@ -168,7 +177,7 @@
     return;
   }
 
-  {# 17. Boolean (fallback) #}
+  {# 16. Boolean (fallback) #}
   {% elif boolean_type %}
   {
     impl.setBoolean(v8Value->BooleanValue());
@@ -176,7 +185,7 @@
   }
 
   {% else %}
-  {# 18. TypeError #}
+  {# 17. TypeError #}
   exceptionState.throwTypeError("The provided value is not of type '{{type_string}}'");
   {% endif %}
 }
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
index 18d113f..0a188884 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
@@ -37,4 +37,6 @@
     Dictionary dictionaryMember;
     [RuntimeEnabled=runtimeFeature] boolean runtimeMember;
     [PrefixGet] object prefixGetMember;
+    record<ByteString, byte> recordMember;
+    record<USVString, TestObject> garbageCollectedRecordMember;
 };
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
index 9f3006e..aa57e1b 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
@@ -42,6 +42,9 @@
 typedef TestInterfaceConstructor   T;
 typedef (TestInterface or TestInterfaceEmpty) TestInterfaceOrTestInterfaceEmpty;
 typedef (DOMString or double) DOMStringOrDouble;
+typedef record<ByteString, long>   SomeRecordType;
+typedef record<USVString, TestObject> RecordWithOilpanValueType;
+typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) UnionWithRecord;
 
 [
     Constructor(String stringArg),
@@ -61,4 +64,9 @@
 
     ArrayOfStrings arrayOfStringsMethodArrayOfStringsArg(ArrayOfStrings arrayOfStringsArg);
     String[] stringArrayMethodStringArrayArg(String[] stringArrayArg);
+
+    void methodTakingRecord(SomeRecordType arg);
+    void methodTakingOilpanValueRecord(RecordWithOilpanValueType arg);
+    double unionWithRecordMethod(UnionWithRecord arg);
+    record<DOMString, boolean> methodThatReturnsRecord();
 };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.cpp b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.cpp
new file mode 100644
index 0000000..e44c4e3
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.cpp
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl
+
+// clang-format off
+#include "ByteStringSequenceSequenceOrByteStringByteStringRecord.h"
+
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/ToV8.h"
+
+namespace blink {
+
+ByteStringSequenceSequenceOrByteStringByteStringRecord::ByteStringSequenceSequenceOrByteStringByteStringRecord() : m_type(SpecificTypeNone) {}
+
+const Vector<Vector<String>>& ByteStringSequenceSequenceOrByteStringByteStringRecord::getAsByteStringSequenceSequence() const {
+  DCHECK(isByteStringSequenceSequence());
+  return m_byteStringSequenceSequence;
+}
+
+void ByteStringSequenceSequenceOrByteStringByteStringRecord::setByteStringSequenceSequence(const Vector<Vector<String>>& value) {
+  DCHECK(isNull());
+  m_byteStringSequenceSequence = value;
+  m_type = SpecificTypeByteStringSequenceSequence;
+}
+
+ByteStringSequenceSequenceOrByteStringByteStringRecord ByteStringSequenceSequenceOrByteStringByteStringRecord::fromByteStringSequenceSequence(const Vector<Vector<String>>& value) {
+  ByteStringSequenceSequenceOrByteStringByteStringRecord container;
+  container.setByteStringSequenceSequence(value);
+  return container;
+}
+
+const Vector<std::pair<String, String>>& ByteStringSequenceSequenceOrByteStringByteStringRecord::getAsByteStringByteStringRecord() const {
+  DCHECK(isByteStringByteStringRecord());
+  return m_byteStringByteStringRecord;
+}
+
+void ByteStringSequenceSequenceOrByteStringByteStringRecord::setByteStringByteStringRecord(const Vector<std::pair<String, String>>& value) {
+  DCHECK(isNull());
+  m_byteStringByteStringRecord = value;
+  m_type = SpecificTypeByteStringByteStringRecord;
+}
+
+ByteStringSequenceSequenceOrByteStringByteStringRecord ByteStringSequenceSequenceOrByteStringByteStringRecord::fromByteStringByteStringRecord(const Vector<std::pair<String, String>>& value) {
+  ByteStringSequenceSequenceOrByteStringByteStringRecord container;
+  container.setByteStringByteStringRecord(value);
+  return container;
+}
+
+ByteStringSequenceSequenceOrByteStringByteStringRecord::ByteStringSequenceSequenceOrByteStringByteStringRecord(const ByteStringSequenceSequenceOrByteStringByteStringRecord&) = default;
+ByteStringSequenceSequenceOrByteStringByteStringRecord::~ByteStringSequenceSequenceOrByteStringByteStringRecord() = default;
+ByteStringSequenceSequenceOrByteStringByteStringRecord& ByteStringSequenceSequenceOrByteStringByteStringRecord::operator=(const ByteStringSequenceSequenceOrByteStringByteStringRecord&) = default;
+
+DEFINE_TRACE(ByteStringSequenceSequenceOrByteStringByteStringRecord) {
+}
+
+void V8ByteStringSequenceSequenceOrByteStringByteStringRecord::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, ByteStringSequenceSequenceOrByteStringByteStringRecord& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) {
+  if (v8Value.IsEmpty())
+    return;
+
+  if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value))
+    return;
+
+  if (v8Value->IsArray()) {
+    Vector<Vector<String>> cppValue = toImplArray<Vector<Vector<String>>>(v8Value, 0, isolate, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setByteStringSequenceSequence(cppValue);
+    return;
+  }
+
+  if (v8Value->IsObject()) {
+    Vector<std::pair<String, String>> cppValue = NativeValueTraits<IDLRecord<IDLByteString, IDLByteString>>::nativeValue(isolate, v8Value, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setByteStringByteStringRecord(cppValue);
+    return;
+  }
+
+  exceptionState.throwTypeError("The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'");
+}
+
+v8::Local<v8::Value> ToV8(const ByteStringSequenceSequenceOrByteStringByteStringRecord& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) {
+  switch (impl.m_type) {
+    case ByteStringSequenceSequenceOrByteStringByteStringRecord::SpecificTypeNone:
+      return v8::Null(isolate);
+    case ByteStringSequenceSequenceOrByteStringByteStringRecord::SpecificTypeByteStringSequenceSequence:
+      return ToV8(impl.getAsByteStringSequenceSequence(), creationContext, isolate);
+    case ByteStringSequenceSequenceOrByteStringByteStringRecord::SpecificTypeByteStringByteStringRecord:
+      return ToV8(impl.getAsByteStringByteStringRecord(), creationContext, isolate);
+    default:
+      NOTREACHED();
+  }
+  return v8::Local<v8::Value>();
+}
+
+ByteStringSequenceSequenceOrByteStringByteStringRecord NativeValueTraits<ByteStringSequenceSequenceOrByteStringByteStringRecord>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
+  ByteStringSequenceSequenceOrByteStringByteStringRecord impl;
+  V8ByteStringSequenceSequenceOrByteStringByteStringRecord::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState);
+  return impl;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
new file mode 100644
index 0000000..a2e89d84
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file has been auto-generated by code_generator_v8.py.
+// DO NOT MODIFY!
+
+// This file has been generated from the Jinja2 template in
+// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl
+
+// clang-format off
+#ifndef ByteStringSequenceSequenceOrByteStringByteStringRecord_h
+#define ByteStringSequenceSequenceOrByteStringByteStringRecord_h
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/NativeValueTraits.h"
+#include "bindings/core/v8/NativeValueTraitsImpl.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "core/CoreExport.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class CORE_EXPORT ByteStringSequenceSequenceOrByteStringByteStringRecord final {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ public:
+  ByteStringSequenceSequenceOrByteStringByteStringRecord();
+  bool isNull() const { return m_type == SpecificTypeNone; }
+
+  bool isByteStringSequenceSequence() const { return m_type == SpecificTypeByteStringSequenceSequence; }
+  const Vector<Vector<String>>& getAsByteStringSequenceSequence() const;
+  void setByteStringSequenceSequence(const Vector<Vector<String>>&);
+  static ByteStringSequenceSequenceOrByteStringByteStringRecord fromByteStringSequenceSequence(const Vector<Vector<String>>&);
+
+  bool isByteStringByteStringRecord() const { return m_type == SpecificTypeByteStringByteStringRecord; }
+  const Vector<std::pair<String, String>>& getAsByteStringByteStringRecord() const;
+  void setByteStringByteStringRecord(const Vector<std::pair<String, String>>&);
+  static ByteStringSequenceSequenceOrByteStringByteStringRecord fromByteStringByteStringRecord(const Vector<std::pair<String, String>>&);
+
+  ByteStringSequenceSequenceOrByteStringByteStringRecord(const ByteStringSequenceSequenceOrByteStringByteStringRecord&);
+  ~ByteStringSequenceSequenceOrByteStringByteStringRecord();
+  ByteStringSequenceSequenceOrByteStringByteStringRecord& operator=(const ByteStringSequenceSequenceOrByteStringByteStringRecord&);
+  DECLARE_TRACE();
+
+ private:
+  enum SpecificTypes {
+    SpecificTypeNone,
+    SpecificTypeByteStringSequenceSequence,
+    SpecificTypeByteStringByteStringRecord,
+  };
+  SpecificTypes m_type;
+
+  Vector<Vector<String>> m_byteStringSequenceSequence;
+  Vector<std::pair<String, String>> m_byteStringByteStringRecord;
+
+  friend CORE_EXPORT v8::Local<v8::Value> ToV8(const ByteStringSequenceSequenceOrByteStringByteStringRecord&, v8::Local<v8::Object>, v8::Isolate*);
+};
+
+class V8ByteStringSequenceSequenceOrByteStringByteStringRecord final {
+ public:
+  CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, ByteStringSequenceSequenceOrByteStringByteStringRecord&, UnionTypeConversionMode, ExceptionState&);
+};
+
+CORE_EXPORT v8::Local<v8::Value> ToV8(const ByteStringSequenceSequenceOrByteStringByteStringRecord&, v8::Local<v8::Object>, v8::Isolate*);
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ByteStringSequenceSequenceOrByteStringByteStringRecord& impl) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
+}
+
+template <class CallbackInfo>
+inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ByteStringSequenceSequenceOrByteStringByteStringRecord& impl, v8::Local<v8::Object> creationContext) {
+  v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate()));
+}
+
+template <>
+struct NativeValueTraits<ByteStringSequenceSequenceOrByteStringByteStringRecord> : public NativeValueTraitsBase<ByteStringSequenceSequenceOrByteStringByteStringRecord> {
+  CORE_EXPORT static ByteStringSequenceSequenceOrByteStringByteStringRecord nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+};
+
+template <>
+struct V8TypeOf<ByteStringSequenceSequenceOrByteStringByteStringRecord> {
+  typedef V8ByteStringSequenceSequenceOrByteStringByteStringRecord Type;
+};
+
+}  // namespace blink
+
+// We need to set canInitializeWithMemset=true because HeapVector supports
+// items that can initialize with memset or have a vtable. It is safe to
+// set canInitializeWithMemset=true for a union type object in practice.
+// See https://codereview.chromium.org/1118993002/#msg5 for more details.
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::ByteStringSequenceSequenceOrByteStringByteStringRecord);
+
+#endif  // ByteStringSequenceSequenceOrByteStringByteStringRecord_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
index 3ae07758..75e4953 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
@@ -156,6 +156,17 @@
 void TestDictionary::setEventTargetMember(EventTarget* value) {
   m_eventTargetMember = value;
 }
+bool TestDictionary::hasGarbageCollectedRecordMember() const {
+  return m_hasGarbageCollectedRecordMember;
+}
+const HeapVector<std::pair<String, Member<TestObject>>>& TestDictionary::garbageCollectedRecordMember() const {
+  DCHECK(m_hasGarbageCollectedRecordMember);
+  return m_garbageCollectedRecordMember;
+}
+void TestDictionary::setGarbageCollectedRecordMember(const HeapVector<std::pair<String, Member<TestObject>>>& value) {
+  m_garbageCollectedRecordMember = value;
+  m_hasGarbageCollectedRecordMember = true;
+}
 bool TestDictionary::hasInternalDictionarySequenceMember() const {
   return m_hasInternalDictionarySequenceMember;
 }
@@ -217,6 +228,17 @@
 void TestDictionary::setPrefixGetMember(ScriptValue value) {
   m_prefixGetMember = value;
 }
+bool TestDictionary::hasRecordMember() const {
+  return m_hasRecordMember;
+}
+const Vector<std::pair<String, int8_t>>& TestDictionary::recordMember() const {
+  DCHECK(m_hasRecordMember);
+  return m_recordMember;
+}
+void TestDictionary::setRecordMember(const Vector<std::pair<String, int8_t>>& value) {
+  m_recordMember = value;
+  m_hasRecordMember = true;
+}
 bool TestDictionary::hasRestrictedDoubleMember() const {
   return m_hasRestrictedDoubleMember;
 }
@@ -392,6 +414,7 @@
   visitor->trace(m_doubleOrStringSequenceMember);
   visitor->trace(m_elementOrNullMember);
   visitor->trace(m_eventTargetMember);
+  visitor->trace(m_garbageCollectedRecordMember);
   visitor->trace(m_internalDictionarySequenceMember);
   visitor->trace(m_otherDoubleOrStringMember);
   visitor->trace(m_testInterface2OrUint8ArrayMember);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
index e7a8276..2f925294 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -87,6 +87,10 @@
   EventTarget* eventTargetMember() const;
   void setEventTargetMember(EventTarget*);
 
+  bool hasGarbageCollectedRecordMember() const;
+  const HeapVector<std::pair<String, Member<TestObject>>>& garbageCollectedRecordMember() const;
+  void setGarbageCollectedRecordMember(const HeapVector<std::pair<String, Member<TestObject>>>&);
+
   bool hasInternalDictionarySequenceMember() const;
   const HeapVector<InternalDictionary>& internalDictionarySequenceMember() const;
   void setInternalDictionarySequenceMember(const HeapVector<InternalDictionary>&);
@@ -112,6 +116,10 @@
   ScriptValue getPrefixGetMember() const;
   void setPrefixGetMember(ScriptValue);
 
+  bool hasRecordMember() const;
+  const Vector<std::pair<String, int8_t>>& recordMember() const;
+  void setRecordMember(const Vector<std::pair<String, int8_t>>&);
+
   bool hasRestrictedDoubleMember() const;
   double restrictedDoubleMember() const;
   void setRestrictedDoubleMember(double);
@@ -199,6 +207,8 @@
   bool m_hasEnumSequenceMember = false;
   Vector<String> m_enumSequenceMember;
   Member<EventTarget> m_eventTargetMember;
+  bool m_hasGarbageCollectedRecordMember = false;
+  HeapVector<std::pair<String, Member<TestObject>>> m_garbageCollectedRecordMember;
   bool m_hasInternalDictionarySequenceMember = false;
   HeapVector<InternalDictionary> m_internalDictionarySequenceMember;
   bool m_hasLongMember = false;
@@ -207,6 +217,8 @@
   ScriptValue m_objectOrNullMember;
   DoubleOrString m_otherDoubleOrStringMember;
   ScriptValue m_prefixGetMember;
+  bool m_hasRecordMember = false;
+  Vector<std::pair<String, int8_t>> m_recordMember;
   bool m_hasRestrictedDoubleMember = false;
   double m_restrictedDoubleMember;
   bool m_hasRuntimeMember = false;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
index 1f009f9..77c16c242 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
@@ -244,6 +244,20 @@
     impl.setEventTargetMember(eventTargetMember);
   }
 
+  v8::Local<v8::Value> garbageCollectedRecordMemberValue;
+  if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "garbageCollectedRecordMember")).ToLocal(&garbageCollectedRecordMemberValue)) {
+    exceptionState.rethrowV8Exception(block.Exception());
+    return;
+  }
+  if (garbageCollectedRecordMemberValue.IsEmpty() || garbageCollectedRecordMemberValue->IsUndefined()) {
+    // Do nothing.
+  } else {
+    HeapVector<std::pair<String, Member<TestObject>>> garbageCollectedRecordMember = NativeValueTraits<IDLRecord<IDLUSVString, TestObject>>::nativeValue(isolate, garbageCollectedRecordMemberValue, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setGarbageCollectedRecordMember(garbageCollectedRecordMember);
+  }
+
   v8::Local<v8::Value> internalDictionarySequenceMemberValue;
   if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "internalDictionarySequenceMember")).ToLocal(&internalDictionarySequenceMemberValue)) {
     exceptionState.rethrowV8Exception(block.Exception());
@@ -337,6 +351,20 @@
     impl.setPrefixGetMember(prefixGetMember);
   }
 
+  v8::Local<v8::Value> recordMemberValue;
+  if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "recordMember")).ToLocal(&recordMemberValue)) {
+    exceptionState.rethrowV8Exception(block.Exception());
+    return;
+  }
+  if (recordMemberValue.IsEmpty() || recordMemberValue->IsUndefined()) {
+    // Do nothing.
+  } else {
+    Vector<std::pair<String, int8_t>> recordMember = NativeValueTraits<IDLRecord<IDLByteString, IDLByte>>::nativeValue(isolate, recordMemberValue, exceptionState);
+    if (exceptionState.hadException())
+      return;
+    impl.setRecordMember(recordMember);
+  }
+
   v8::Local<v8::Value> restrictedDoubleMemberValue;
   if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "restrictedDoubleMember")).ToLocal(&restrictedDoubleMemberValue)) {
     exceptionState.rethrowV8Exception(block.Exception());
@@ -665,6 +693,11 @@
       return false;
   }
 
+  if (impl.hasGarbageCollectedRecordMember()) {
+    if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "garbageCollectedRecordMember"), ToV8(impl.garbageCollectedRecordMember(), creationContext, isolate))))
+      return false;
+  }
+
   if (impl.hasInternalDictionarySequenceMember()) {
     if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "internalDictionarySequenceMember"), ToV8(impl.internalDictionarySequenceMember(), creationContext, isolate))))
       return false;
@@ -707,6 +740,11 @@
       return false;
   }
 
+  if (impl.hasRecordMember()) {
+    if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "recordMember"), ToV8(impl.recordMember(), creationContext, isolate))))
+      return false;
+  }
+
   if (impl.hasRestrictedDoubleMember()) {
     if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "restrictedDoubleMember"), v8::Number::New(isolate, impl.restrictedDoubleMember()))))
       return false;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
index 17eda00..86e0684 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
@@ -11,6 +11,7 @@
 // clang-format off
 #include "V8TestTypedefs.h"
 
+#include "bindings/core/v8/ByteStringSequenceSequenceOrByteStringByteStringRecord.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
 #include "bindings/core/v8/NativeValueTraitsImpl.h"
@@ -21,6 +22,7 @@
 #include "bindings/core/v8/V8TestCallbackInterface.h"
 #include "bindings/core/v8/V8TestInterface.h"
 #include "bindings/core/v8/V8TestInterfaceEmpty.h"
+#include "bindings/core/v8/V8TestObject.h"
 #include "core/dom/Document.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "wtf/GetPtr.h"
@@ -242,6 +244,66 @@
   v8SetReturnValue(info, ToV8(impl->stringArrayMethodStringArrayArg(stringArrayArg), info.Holder(), info.GetIsolate()));
 }
 
+static void methodTakingRecordMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestTypedefs", "methodTakingRecord");
+
+  TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  Vector<std::pair<String, int32_t>> arg;
+  arg = NativeValueTraits<IDLRecord<IDLByteString, IDLLong>>::nativeValue(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->methodTakingRecord(arg);
+}
+
+static void methodTakingOilpanValueRecordMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestTypedefs", "methodTakingOilpanValueRecord");
+
+  TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  HeapVector<std::pair<String, Member<TestObject>>> arg;
+  arg = NativeValueTraits<IDLRecord<IDLUSVString, TestObject>>::nativeValue(info.GetIsolate(), info[0], exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  impl->methodTakingOilpanValueRecord(arg);
+}
+
+static void unionWithRecordMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestTypedefs", "unionWithRecordMethod");
+
+  TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder());
+
+  if (UNLIKELY(info.Length() < 1)) {
+    exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length()));
+    return;
+  }
+
+  ByteStringSequenceSequenceOrByteStringByteStringRecord arg;
+  V8ByteStringSequenceSequenceOrByteStringByteStringRecord::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  v8SetReturnValue(info, impl->unionWithRecordMethod(arg));
+}
+
+static void methodThatReturnsRecordMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder());
+
+  v8SetReturnValue(info, ToV8(impl->methodThatReturnsRecord(), info.Holder(), info.GetIsolate()));
+}
+
 static void constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
   if (UNLIKELY(info.Length() < 1)) {
     V8ThrowException::throwTypeError(info.GetIsolate(), ExceptionMessages::failedToConstruct("TestTypedefs", ExceptionMessages::notEnoughArguments(1, info.Length())));
@@ -313,6 +375,22 @@
   TestTypedefsV8Internal::stringArrayMethodStringArrayArgMethod(info);
 }
 
+void V8TestTypedefs::methodTakingRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefsV8Internal::methodTakingRecordMethod(info);
+}
+
+void V8TestTypedefs::methodTakingOilpanValueRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefsV8Internal::methodTakingOilpanValueRecordMethod(info);
+}
+
+void V8TestTypedefs::unionWithRecordMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefsV8Internal::unionWithRecordMethodMethod(info);
+}
+
+void V8TestTypedefs::methodThatReturnsRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  TestTypedefsV8Internal::methodThatReturnsRecordMethod(info);
+}
+
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
 #if defined(COMPONENT_BUILD) && defined(WIN32) && COMPILER(CLANG)
@@ -340,6 +418,10 @@
     {"domStringOrDoubleMethod", V8TestTypedefs::domStringOrDoubleMethodMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"arrayOfStringsMethodArrayOfStringsArg", V8TestTypedefs::arrayOfStringsMethodArrayOfStringsArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
     {"stringArrayMethodStringArrayArg", V8TestTypedefs::stringArrayMethodStringArrayArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"methodTakingRecord", V8TestTypedefs::methodTakingRecordMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"methodTakingOilpanValueRecord", V8TestTypedefs::methodTakingOilpanValueRecordMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"unionWithRecordMethod", V8TestTypedefs::unionWithRecordMethodMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
+    {"methodThatReturnsRecord", V8TestTypedefs::methodThatReturnsRecordMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
 };
 
 void V8TestTypedefs::constructorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
index c1a1f787..5d88480 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
@@ -12,6 +12,7 @@
 #ifndef V8TestTypedefs_h
 #define V8TestTypedefs_h
 
+#include "bindings/core/v8/ByteStringSequenceSequenceOrByteStringByteStringRecord.h"
 #include "bindings/core/v8/GeneratedCodeHelper.h"
 #include "bindings/core/v8/NativeValueTraits.h"
 #include "bindings/core/v8/ScriptWrappable.h"
@@ -62,6 +63,10 @@
   CORE_EXPORT static void domStringOrDoubleMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void arrayOfStringsMethodArrayOfStringsArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void stringArrayMethodStringArrayArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void methodTakingRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void methodTakingOilpanValueRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void unionWithRecordMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void methodThatReturnsRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
 };
 
 template <>
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index 1ac90f3..8d6ee0e8 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -58,7 +58,7 @@
         'property_name',
         # Name of the type (e.g. EClear, int)
         'type_name',
-        # Affects how the field is generated (keyword, flag)
+        # Affects how the field is generated (keyword, flag, monotonic_flag)
         'field_template',
         # Bits needed for storage
         'size',
@@ -212,7 +212,7 @@
         name=member_name,
         property_name=field_name,
         type_name='bool',
-        field_template='flag',
+        field_template='monotonic_flag',
         size=1,
         default_value='false',
         getter_method_name=field_name,
diff --git a/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl b/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
index cb055de9..34c45b7 100644
--- a/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
@@ -10,11 +10,13 @@
 #include "{{path}}"
 {% endfor %}
 
-{# Returns the default value for the field, converted to fit in the storage container. #}
-{% macro default_value(field) -%}
-{# We only support enum fields for now. #}
-static_cast<unsigned>({{field.default_value}})
-{%- endmacro %}
+{# Each field template has macros that we can call to generate specific
+   aspects of the field (e.g. getters, setters).
+#}
+{% import 'fields/keyword.tmpl' as keyword %}
+{% import 'fields/flag.tmpl' as flag %}
+{% import 'fields/monotonic_flag.tmpl' as monotonic_flag %}
+{% set field_templates = { 'keyword': keyword, 'flag': flag, 'monotonic_flag': monotonic_flag } %}
 
 namespace blink {
 
@@ -24,7 +26,7 @@
  public:
   ALWAYS_INLINE ComputedStyleBase() :
   {% for field in fields %}
-      {{field.name}}({{default_value(field)}}){{print_if(not loop.last, ',')}}
+      {{field.name}}(static_cast<unsigned>({{field.default_value}})){{print_if(not loop.last, ',')}}
   {% endfor %}
   {}
   ~ComputedStyleBase() {}
@@ -87,24 +89,7 @@
 
   {% for field in fields %}
   // {{field.property_name}}
-  inline static {{field.type_name}} {{field.initial_method_name}}() {
-    return {{field.default_value}};
-  }
-  {{field.type_name}} {{field.getter_method_name}}() const {
-    return static_cast<{{field.type_name}}>({{field.name}});
-  }
-  {% if field.is_nonproperty %}
-  void {{field.setter_method_name}}() {
-    {{field.name}} = static_cast<unsigned>(true);
-  }
-  {% else %}
-  void {{field.setter_method_name}}({{field.type_name}} v) {
-    {{field.name}} = static_cast<unsigned>(v);
-  }
-  {% endif %}
-  inline void {{field.resetter_method_name}}() {
-    {{field.name}} = {{default_value(field)}};
-  }
+  {{field_templates[field.field_template].decl_methods(field)|indent(2)}}
 
   {% endfor %}
  protected:
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl
new file mode 100644
index 0000000..0685305
--- /dev/null
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl
@@ -0,0 +1,15 @@
+{% macro decl_methods(field) -%}
+inline static {{field.type_name}} {{field.initial_method_name}}() {
+  return {{field.default_value}};
+}
+{{field.type_name}} {{field.getter_method_name}}() const {
+  return static_cast<{{field.type_name}}>({{field.name}});
+}
+void {{field.setter_method_name}}({{field.type_name}} v) {
+  {{field.name}} = static_cast<unsigned>(v);
+}
+inline void {{field.resetter_method_name}}() {
+  {{field.name}} = static_cast<unsigned>({{field.default_value}});
+}
+{%- endmacro %}
+
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/flag.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/flag.tmpl
new file mode 100644
index 0000000..30519af
--- /dev/null
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/flag.tmpl
@@ -0,0 +1,4 @@
+{% import 'fields/base.tmpl' as base %}
+{% macro decl_methods(field) -%}
+{{ base.decl_methods(field) }}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl
new file mode 100644
index 0000000..3d6cb329
--- /dev/null
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl
@@ -0,0 +1,5 @@
+{% import 'fields/base.tmpl' as base %}
+{% macro decl_methods(field) -%}
+{{ base.decl_methods(field) }}
+{# TODO(shend): Add special keyword methods like cardinality #}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl
new file mode 100644
index 0000000..84c47ab
--- /dev/null
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl
@@ -0,0 +1,8 @@
+{% macro decl_methods(field) %}
+bool {{field.getter_method_name}}() const {
+  return {{field.name}};
+}
+void {{field.setter_method_name}}() {
+  {{field.name}} = static_cast<unsigned>(true);
+}
+{% endmacro %}
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 23398e8..898288d 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -266,6 +266,8 @@
     "testing/OriginTrialsTest.cpp",
     "testing/OriginTrialsTest.h",
     "testing/OriginTrialsTestPartial.h",
+    "testing/RecordTest.cpp",
+    "testing/RecordTest.h",
     "testing/TypeConversions.h",
     "testing/UnionTypesTest.cpp",
     "testing/UnionTypesTest.h",
@@ -403,6 +405,10 @@
 css_properties("make_core_generated_computed_style_base") {
   script = "../build/scripts/make_computed_style_base.py"
   other_inputs = [
+    "../build/scripts/templates/fields/base.tmpl",
+    "../build/scripts/templates/fields/keyword.tmpl",
+    "../build/scripts/templates/fields/flag.tmpl",
+    "../build/scripts/templates/fields/monotonic_flag.tmpl",
     "../build/scripts/templates/ComputedStyleBase.cpp.tmpl",
     "../build/scripts/templates/ComputedStyleBase.h.tmpl",
     "../build/scripts/templates/ComputedStyleBaseConstants.h.tmpl",
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index 4a301d42..5f02fc6 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -605,6 +605,7 @@
                     "testing/LayerRect.idl",
                     "testing/LayerRectList.idl",
                     "testing/OriginTrialsTest.idl",
+                    "testing/RecordTest.idl",
                     "testing/TypeConversions.idl",
                     "testing/UnionTypesTest.idl",
                   ],
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 7768ed42..e945bd9 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -66,6 +66,9 @@
         // Field is stored a packed boolean flag and has a initial/getter/setter/resetter.
         // TODO(shend): generalise this to "primitive"
         "flag",
+        // Field is stored as a bool, whose default value is false
+        // and can only be set to true. Has a initial/getter/setter.
+        "monotonic_flag"
       ],
     },
 
diff --git a/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.cpp b/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.cpp
index a502a2595..1fbcd95 100644
--- a/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.cpp
+++ b/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.cpp
@@ -234,28 +234,69 @@
   return nextSkippingChildren(node, stayWithin);
 }
 
-static LayoutObject* nextSiblingLayoutObjectInternal(Node* node,
-                                                     int32_t& limit) {
+static Node* nextLayoutSiblingInternal(Node* node, int32_t& limit) {
   for (Node* sibling = node; sibling && limit-- != 0;
        sibling = LayoutTreeBuilderTraversal::nextSibling(*sibling)) {
-    LayoutObject* layoutObject = sibling->layoutObject();
+    if (!hasDisplayContentsStyle(*sibling))
+      return sibling;
 
-#if DCHECK_IS_ON()
-    if (hasDisplayContentsStyle(*sibling))
-      DCHECK(!layoutObject);
-#endif
+    if (Node* inner =
+            nextLayoutSiblingInternal(pseudoAwareFirstChild(*sibling), limit))
+      return inner;
 
-    if (!layoutObject && hasDisplayContentsStyle(*sibling)) {
-      layoutObject = nextSiblingLayoutObjectInternal(
-          pseudoAwareFirstChild(*sibling), limit);
-      if (layoutObject)
-        return layoutObject;
-      if (limit == -1)
-        return nullptr;
-    }
+    if (limit == -1)
+      return nullptr;
+  }
 
-    if (layoutObject && !isLayoutObjectReparented(layoutObject))
-      return layoutObject;
+  return nullptr;
+}
+
+Node* LayoutTreeBuilderTraversal::nextLayoutSibling(const Node& node,
+                                                    int32_t& limit) {
+  DCHECK_NE(limit, -1);
+  if (Node* sibling = nextLayoutSiblingInternal(nextSibling(node), limit))
+    return sibling;
+
+  Node* parent = LayoutTreeBuilderTraversal::parent(node);
+  while (limit != -1 && parent && hasDisplayContentsStyle(*parent)) {
+    if (Node* sibling = nextLayoutSiblingInternal(nextSibling(*parent), limit))
+      return sibling;
+    parent = LayoutTreeBuilderTraversal::parent(*parent);
+  }
+
+  return nullptr;
+}
+
+static Node* previousLayoutSiblingInternal(Node* node, int32_t& limit) {
+  for (Node* sibling = node; sibling && limit-- != 0;
+       sibling = LayoutTreeBuilderTraversal::previousSibling(*sibling)) {
+    if (!hasDisplayContentsStyle(*sibling))
+      return sibling;
+
+    if (Node* inner = previousLayoutSiblingInternal(
+            pseudoAwareLastChild(*sibling), limit))
+      return inner;
+
+    if (limit == -1)
+      return nullptr;
+  }
+
+  return nullptr;
+}
+
+Node* LayoutTreeBuilderTraversal::previousLayoutSibling(const Node& node,
+                                                        int32_t& limit) {
+  DCHECK_NE(limit, -1);
+  if (Node* sibling =
+          previousLayoutSiblingInternal(previousSibling(node), limit))
+    return sibling;
+
+  Node* parent = LayoutTreeBuilderTraversal::parent(node);
+  while (limit != -1 && parent && hasDisplayContentsStyle(*parent)) {
+    if (Node* sibling =
+            previousLayoutSiblingInternal(previousSibling(*parent), limit))
+      return sibling;
+    parent = LayoutTreeBuilderTraversal::parent(*parent);
   }
 
   return nullptr;
@@ -265,45 +306,12 @@
     const Node& node,
     int32_t limit) {
   DCHECK(limit == kTraverseAllSiblings || limit >= 0) << limit;
-  if (LayoutObject* sibling =
-          nextSiblingLayoutObjectInternal(nextSibling(node), limit))
-    return sibling;
-
-  Node* parent = LayoutTreeBuilderTraversal::parent(node);
-  while (limit != -1 && parent && hasDisplayContentsStyle(*parent)) {
-    if (LayoutObject* sibling =
-            nextSiblingLayoutObjectInternal(nextSibling(*parent), limit))
-      return sibling;
-    parent = LayoutTreeBuilderTraversal::parent(*parent);
-  }
-
-  return nullptr;
-}
-
-static LayoutObject* previousSiblingLayoutObjectInternal(Node* node,
-                                                         int32_t& limit) {
-  for (Node* sibling = node; sibling && limit-- != 0;
-       sibling = LayoutTreeBuilderTraversal::previousSibling(*sibling)) {
+  for (Node* sibling = nextLayoutSibling(node, limit); sibling && limit != -1;
+       sibling = nextLayoutSibling(*sibling, limit)) {
     LayoutObject* layoutObject = sibling->layoutObject();
-
-#if DCHECK_IS_ON()
-    if (hasDisplayContentsStyle(*sibling))
-      DCHECK(!layoutObject);
-#endif
-
-    if (!layoutObject && hasDisplayContentsStyle(*sibling)) {
-      layoutObject = previousSiblingLayoutObjectInternal(
-          pseudoAwareLastChild(*sibling), limit);
-      if (layoutObject)
-        return layoutObject;
-      if (limit == -1)
-        return nullptr;
-    }
-
     if (layoutObject && !isLayoutObjectReparented(layoutObject))
       return layoutObject;
   }
-
   return nullptr;
 }
 
@@ -311,18 +319,13 @@
     const Node& node,
     int32_t limit) {
   DCHECK(limit == kTraverseAllSiblings || limit >= 0) << limit;
-  if (LayoutObject* sibling =
-          previousSiblingLayoutObjectInternal(previousSibling(node), limit))
-    return sibling;
-
-  Node* parent = LayoutTreeBuilderTraversal::parent(node);
-  while (limit != -1 && parent && hasDisplayContentsStyle(*parent)) {
-    if (LayoutObject* sibling = previousSiblingLayoutObjectInternal(
-            previousSibling(*parent), limit))
-      return sibling;
-    parent = LayoutTreeBuilderTraversal::parent(*parent);
+  for (Node* sibling = previousLayoutSibling(node, limit);
+       sibling && limit != -1;
+       sibling = previousLayoutSibling(*sibling, limit)) {
+    LayoutObject* layoutObject = sibling->layoutObject();
+    if (layoutObject && !isLayoutObjectReparented(layoutObject))
+      return layoutObject;
   }
-
   return nullptr;
 }
 
diff --git a/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.h b/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.h
index 2d88e16..0053fc9d 100644
--- a/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.h
+++ b/third_party/WebKit/Source/core/dom/LayoutTreeBuilderTraversal.h
@@ -61,6 +61,14 @@
   static ContainerNode* layoutParent(const Node&, ParentDetails* = nullptr);
   static Node* firstChild(const Node&);
   static Node* nextSibling(const Node&);
+  static Node* nextLayoutSibling(const Node& node) {
+    int32_t limit = kTraverseAllSiblings;
+    return nextLayoutSibling(node, limit);
+  }
+  static Node* previousLayoutSibling(const Node& node) {
+    int32_t limit = kTraverseAllSiblings;
+    return previousLayoutSibling(node, limit);
+  }
   static Node* previousSibling(const Node&);
   static Node* previous(const Node&, const Node* stayWithin);
   static Node* next(const Node&, const Node* stayWithin);
@@ -78,6 +86,10 @@
     ContainerNode* found = parent(node);
     return found && found->isElementNode() ? toElement(found) : 0;
   }
+
+ private:
+  static Node* nextLayoutSibling(const Node&, int32_t& limit);
+  static Node* previousLayoutSibling(const Node&, int32_t& limit);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameClient.h b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
index 3cf25b46..714313a 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
@@ -299,10 +299,6 @@
   virtual std::unique_ptr<WebServiceWorkerProvider>
   createServiceWorkerProvider() = 0;
 
-  virtual bool isControlledByServiceWorker(DocumentLoader&) = 0;
-
-  virtual int64_t serviceWorkerID(DocumentLoader&) = 0;
-
   virtual SharedWorkerRepositoryClient* sharedWorkerRepositoryClient() {
     return 0;
   }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp b/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
index 72d1e4fd4..09e5c92 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
@@ -44,12 +44,7 @@
       m_firstLetterPseudoElement(nullptr) {}
 
 LayoutTextFragment::LayoutTextFragment(Node* node, StringImpl* str)
-    : LayoutText(node, str),
-      m_start(0),
-      m_fragmentLength(str ? str->length() : 0),
-      m_isRemainingTextLayoutObject(false),
-      m_contentString(str),
-      m_firstLetterPseudoElement(nullptr) {}
+    : LayoutTextFragment(node, str, 0, str ? str->length() : 0) {}
 
 LayoutTextFragment::~LayoutTextFragment() {
   ASSERT(!m_firstLetterPseudoElement);
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index 5942857..c6659cb 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -73,6 +73,7 @@
 #include "platform/weborigin/SchemeRegistry.h"
 #include "platform/weborigin/SecurityPolicy.h"
 #include "public/platform/Platform.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "wtf/Assertions.h"
 #include "wtf/AutoReset.h"
 #include "wtf/text/WTFString.h"
@@ -229,6 +230,11 @@
   return resource;
 }
 
+void DocumentLoader::setServiceWorkerNetworkProvider(
+    std::unique_ptr<WebServiceWorkerNetworkProvider> provider) {
+  m_serviceWorkerNetworkProvider = std::move(provider);
+}
+
 void DocumentLoader::dispatchLinkHeaderPreloads(
     ViewportDescriptionWrapper* viewport,
     LinkLoader::MediaPreloadPolicy mediaPolicy) {
@@ -652,6 +658,7 @@
   m_fetcher->clearContext();
   m_applicationCacheHost->detachFromDocumentLoader();
   m_applicationCacheHost.clear();
+  m_serviceWorkerNetworkProvider = nullptr;
   WeakIdentifierMap<DocumentLoader>::notifyObjectDestroyed(this);
   clearMainResourceHandle();
   m_frame = nullptr;
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.h b/third_party/WebKit/Source/core/loader/DocumentLoader.h
index caa44f8..112552f 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.h
@@ -64,6 +64,7 @@
 class LocalFrameClient;
 class FrameLoader;
 class ResourceTimingInfo;
+class WebServiceWorkerNetworkProvider;
 struct ViewportDescriptionWrapper;
 
 class CORE_EXPORT DocumentLoader
@@ -189,6 +190,16 @@
 
   Resource* startPreload(Resource::Type, FetchRequest&);
 
+  void setServiceWorkerNetworkProvider(
+      std::unique_ptr<WebServiceWorkerNetworkProvider>);
+
+  // May return null before the first HTML tag is inserted by the
+  // parser (before didCreateDataSource is called), after the document
+  // is detached from frame, or in tests.
+  WebServiceWorkerNetworkProvider* getServiceWorkerNetworkProvider() {
+    return m_serviceWorkerNetworkProvider.get();
+  }
+
   DECLARE_VIRTUAL_TRACE();
 
  protected:
@@ -283,6 +294,9 @@
 
   Member<ApplicationCacheHost> m_applicationCacheHost;
 
+  std::unique_ptr<WebServiceWorkerNetworkProvider>
+      m_serviceWorkerNetworkProvider;
+
   Member<ContentSecurityPolicy> m_contentSecurityPolicy;
   ClientHintsPreferences m_clientHintsPreferences;
   InitialScrollState m_initialScrollState;
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h
index 354c8eb..5c63535 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.h
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -361,8 +361,6 @@
 
   std::unique_ptr<WebServiceWorkerProvider> createServiceWorkerProvider()
       override;
-  bool isControlledByServiceWorker(DocumentLoader&) override { return false; }
-  int64_t serviceWorkerID(DocumentLoader&) override { return -1; }
   std::unique_ptr<WebApplicationCacheHost> createApplicationCacheHost(
       WebApplicationCacheHostClient*) override;
 
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 9ccd820..b62fe64 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -82,6 +82,7 @@
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/platform/WebViewScheduler.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "wtf/Vector.h"
 
 namespace blink {
@@ -823,13 +824,18 @@
   if (getSecurityOrigin() && getSecurityOrigin()->hasSuborigin())
     return false;
 
-  return localFrameClient()->isControlledByServiceWorker(
-      *masterDocumentLoader());
+  auto* service_worker_network_provider =
+      masterDocumentLoader()->getServiceWorkerNetworkProvider();
+  return service_worker_network_provider &&
+         service_worker_network_provider->isControlledByServiceWorker();
 }
 
 int64_t FrameFetchContext::serviceWorkerID() const {
   DCHECK(masterDocumentLoader());
-  return localFrameClient()->serviceWorkerID(*masterDocumentLoader());
+  auto* service_worker_network_provider =
+      masterDocumentLoader()->getServiceWorkerNetworkProvider();
+  return service_worker_network_provider &&
+         service_worker_network_provider->serviceWorkerID();
 }
 
 bool FrameFetchContext::isMainFrame() const {
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 0b2a00a..c62bf54 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -100,6 +100,7 @@
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebURLRequest.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "wtf/AutoReset.h"
 #include "wtf/text/CString.h"
 #include "wtf/text/WTFString.h"
@@ -486,7 +487,9 @@
 
   // didObserveLoadingBehavior() must be called after dispatchDidCommitLoad() is
   // called for the metrics tracking logic to handle it properly.
-  if (client()->isControlledByServiceWorker(*m_documentLoader)) {
+  if (m_documentLoader->getServiceWorkerNetworkProvider() &&
+      m_documentLoader->getServiceWorkerNetworkProvider()
+          ->isControlledByServiceWorker()) {
     client()->didObserveLoadingBehavior(
         WebLoadingBehaviorServiceWorkerControlled);
   }
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index 7c57d5d2..11e72f6f 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -122,6 +122,7 @@
 #include "core/testing/LayerRectList.h"
 #include "core/testing/MockHyphenation.h"
 #include "core/testing/OriginTrialsTest.h"
+#include "core/testing/RecordTest.h"
 #include "core/testing/TypeConversions.h"
 #include "core/testing/UnionTypesTest.h"
 #include "core/workers/WorkerThread.h"
@@ -2365,6 +2366,10 @@
   return DictionaryTest::create();
 }
 
+RecordTest* Internals::recordTest() const {
+  return RecordTest::create();
+}
+
 UnionTypesTest* Internals::unionTypesTest() const {
   return UnionTypesTest::create();
 }
diff --git a/third_party/WebKit/Source/core/testing/Internals.h b/third_party/WebKit/Source/core/testing/Internals.h
index 8565c296..f62929b 100644
--- a/third_party/WebKit/Source/core/testing/Internals.h
+++ b/third_party/WebKit/Source/core/testing/Internals.h
@@ -68,6 +68,7 @@
 class OriginTrialsTest;
 class Page;
 class Range;
+class RecordTest;
 class SerializedScriptValue;
 class ShadowRoot;
 class TypeConversions;
@@ -380,6 +381,7 @@
       const String& scheme);
 
   TypeConversions* typeConversions() const;
+  RecordTest* recordTest() const;
   DictionaryTest* dictionaryTest() const;
   UnionTypesTest* unionTypesTest() const;
   OriginTrialsTest* originTrialsTest() const;
diff --git a/third_party/WebKit/Source/core/testing/Internals.idl b/third_party/WebKit/Source/core/testing/Internals.idl
index 56ebdf7..cafc80a 100644
--- a/third_party/WebKit/Source/core/testing/Internals.idl
+++ b/third_party/WebKit/Source/core/testing/Internals.idl
@@ -319,6 +319,7 @@
     unsigned long canvasFontCacheMaxFonts();
 
     DictionaryTest dictionaryTest();
+    RecordTest recordTest();
     UnionTypesTest unionTypesTest();
     CallbackFunctionTest callbackFunctionTest();
     [RaisesException] void setScrollChain(ScrollState scrollState, sequence<Element> elements);
diff --git a/third_party/WebKit/Source/core/testing/RecordTest.cpp b/third_party/WebKit/Source/core/testing/RecordTest.cpp
new file mode 100644
index 0000000..f4e79e6
--- /dev/null
+++ b/third_party/WebKit/Source/core/testing/RecordTest.cpp
@@ -0,0 +1,83 @@
+// 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 "RecordTest.h"
+
+namespace blink {
+
+RecordTest::RecordTest() {}
+
+RecordTest::~RecordTest() {}
+
+void RecordTest::setStringLongRecord(
+    const Vector<std::pair<String, int32_t>>& arg) {
+  m_stringLongRecord = arg;
+}
+
+Vector<std::pair<String, int32_t>> RecordTest::getStringLongRecord() {
+  return m_stringLongRecord;
+}
+
+void RecordTest::setNullableStringLongRecord(
+    const Nullable<Vector<std::pair<String, int32_t>>>& arg) {
+  m_nullableStringLongRecord = arg;
+}
+
+Nullable<Vector<std::pair<String, int32_t>>>
+RecordTest::getNullableStringLongRecord() {
+  return m_nullableStringLongRecord;
+}
+
+Vector<std::pair<String, String>> RecordTest::getByteStringByteStringRecord() {
+  return m_byteStringByteStringRecord;
+}
+
+void RecordTest::setByteStringByteStringRecord(
+    const Vector<std::pair<String, String>>& arg) {
+  m_byteStringByteStringRecord = arg;
+}
+
+void RecordTest::setStringElementRecord(
+    const HeapVector<std::pair<String, Member<Element>>>& arg) {
+  m_stringElementRecord = arg;
+}
+
+HeapVector<std::pair<String, Member<Element>>>
+RecordTest::getStringElementRecord() {
+  return m_stringElementRecord;
+}
+
+void RecordTest::setUSVStringUSVStringBooleanRecordRecord(
+    const RecordTest::NestedRecordType& arg) {
+  m_USVStringUSVStringBooleanRecordRecord = arg;
+}
+
+RecordTest::NestedRecordType
+RecordTest::getUSVStringUSVStringBooleanRecordRecord() {
+  return m_USVStringUSVStringBooleanRecordRecord;
+}
+
+Vector<std::pair<String, Vector<String>>>
+RecordTest::returnStringByteStringSequenceRecord() {
+  Vector<std::pair<String, Vector<String>>> record;
+  Vector<String> innerVector1;
+  innerVector1.push_back("hello, world");
+  innerVector1.push_back("hi, mom");
+  record.push_back(std::make_pair(String("foo"), innerVector1));
+  Vector<String> innerVector2;
+  innerVector2.push_back("goodbye, mom");
+  record.push_back(std::make_pair(String("bar"), innerVector2));
+  return record;
+}
+
+bool RecordTest::unionReceivedARecord(
+    const BooleanOrByteStringByteStringRecord& arg) {
+  return arg.isByteStringByteStringRecord();
+}
+
+DEFINE_TRACE(RecordTest) {
+  visitor->trace(m_stringElementRecord);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/testing/RecordTest.h b/third_party/WebKit/Source/core/testing/RecordTest.h
new file mode 100644
index 0000000..ceb6f87fe
--- /dev/null
+++ b/third_party/WebKit/Source/core/testing/RecordTest.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RecordTest_h
+#define RecordTest_h
+
+#include <utility>
+#include "bindings/core/v8/BooleanOrByteStringByteStringRecord.h"
+#include "bindings/core/v8/FloatOrStringElementRecord.h"
+#include "bindings/core/v8/Nullable.h"
+#include "bindings/core/v8/ScriptWrappable.h"
+#include "core/dom/Element.h"
+#include "platform/heap/Handle.h"
+#include "wtf/Vector.h"
+#include "wtf/text/WTFString.h"
+
+namespace blink {
+
+class RecordTest final : public GarbageCollectedFinalized<RecordTest>,
+                         public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static RecordTest* create() { return new RecordTest; }
+  ~RecordTest();
+
+  void setStringLongRecord(const Vector<std::pair<String, int32_t>>& arg);
+  Vector<std::pair<String, int32_t>> getStringLongRecord();
+
+  void setNullableStringLongRecord(
+      const Nullable<Vector<std::pair<String, int32_t>>>& arg);
+  Nullable<Vector<std::pair<String, int32_t>>> getNullableStringLongRecord();
+
+  Vector<std::pair<String, String>> getByteStringByteStringRecord();
+  void setByteStringByteStringRecord(
+      const Vector<std::pair<String, String>>& arg);
+
+  void setStringElementRecord(
+      const HeapVector<std::pair<String, Member<Element>>>& arg);
+  HeapVector<std::pair<String, Member<Element>>> getStringElementRecord();
+
+  using NestedRecordType =
+      Vector<std::pair<String, Vector<std::pair<String, bool>>>>;
+  void setUSVStringUSVStringBooleanRecordRecord(const NestedRecordType& arg);
+  NestedRecordType getUSVStringUSVStringBooleanRecordRecord();
+
+  Vector<std::pair<String, Vector<String>>>
+  returnStringByteStringSequenceRecord();
+
+  bool unionReceivedARecord(const BooleanOrByteStringByteStringRecord& arg);
+
+  void setFloatOrStringElementRecord(const FloatOrStringElementRecord&){};
+
+  DECLARE_TRACE();
+
+ private:
+  RecordTest();
+
+  Vector<std::pair<String, int32_t>> m_stringLongRecord;
+  Nullable<Vector<std::pair<String, int32_t>>> m_nullableStringLongRecord;
+  Vector<std::pair<String, String>> m_byteStringByteStringRecord;
+  HeapVector<std::pair<String, Member<Element>>> m_stringElementRecord;
+  NestedRecordType m_USVStringUSVStringBooleanRecordRecord;
+};
+
+}  // namespace blink
+
+#endif  // RecordTest_h
diff --git a/third_party/WebKit/Source/core/testing/RecordTest.idl b/third_party/WebKit/Source/core/testing/RecordTest.idl
new file mode 100644
index 0000000..4d2b153
--- /dev/null
+++ b/third_party/WebKit/Source/core/testing/RecordTest.idl
@@ -0,0 +1,29 @@
+// 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.
+
+typedef record<DOMString, long> RecordTypedef;
+
+interface RecordTest {
+    void setStringLongRecord(RecordTypedef arg);
+    RecordTypedef getStringLongRecord();
+
+    void setNullableStringLongRecord(RecordTypedef? arg);
+    RecordTypedef? getNullableStringLongRecord();
+
+    void setByteStringByteStringRecord(record<ByteString, ByteString> arg);
+
+    void setStringElementRecord(record<DOMString, Element> arg);
+    record<DOMString, Element> getStringElementRecord();
+
+    void setUSVStringUSVStringBooleanRecordRecord(record<USVString, record<USVString, boolean>> arg);
+    record<USVString, record<USVString, boolean>> getUSVStringUSVStringBooleanRecordRecord();
+
+    record<DOMString, sequence<ByteString>> returnStringByteStringSequenceRecord();
+
+    boolean unionReceivedARecord((boolean or record<ByteString, ByteString>) arg);
+
+    // Test the bindings generator code: unions and dictionaries must trace a
+    // record when it's represented as a HeapVector.
+    void setFloatOrStringElementRecord((float or record<DOMString, Element>) arg);
+};
diff --git a/third_party/WebKit/Source/modules/BUILD.gn b/third_party/WebKit/Source/modules/BUILD.gn
index 342b6c2e..1d05da24 100644
--- a/third_party/WebKit/Source/modules/BUILD.gn
+++ b/third_party/WebKit/Source/modules/BUILD.gn
@@ -93,6 +93,7 @@
     "//third_party/WebKit/Source/modules/accessibility",
     "//third_party/WebKit/Source/modules/app_banner",
     "//third_party/WebKit/Source/modules/audio_output_devices",
+    "//third_party/WebKit/Source/modules/background_fetch",
     "//third_party/WebKit/Source/modules/background_sync",
     "//third_party/WebKit/Source/modules/battery",
     "//third_party/WebKit/Source/modules/beacon",
diff --git a/third_party/WebKit/Source/modules/background_fetch/BUILD.gn b/third_party/WebKit/Source/modules/background_fetch/BUILD.gn
new file mode 100644
index 0000000..33d44004
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/WebKit/Source/modules/modules.gni")
+
+blink_modules_sources("background_fetch") {
+  sources = [
+    "BackgroundFetchManager.cpp",
+    "BackgroundFetchManager.h",
+    "ServiceWorkerRegistrationBackgroundFetch.cpp",
+    "ServiceWorkerRegistrationBackgroundFetch.h",
+  ]
+}
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
new file mode 100644
index 0000000..27fabfc
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
@@ -0,0 +1,29 @@
+// 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 "modules/background_fetch/BackgroundFetchManager.h"
+
+#include "bindings/core/v8/ScriptState.h"
+#include "core/dom/DOMException.h"
+#include "modules/serviceworkers/ServiceWorkerRegistration.h"
+
+namespace blink {
+
+BackgroundFetchManager::BackgroundFetchManager(
+    ServiceWorkerRegistration* registration)
+    : m_registration(registration) {
+  DCHECK(registration);
+}
+
+ScriptPromise BackgroundFetchManager::getTags(ScriptState* scriptState) {
+  return ScriptPromise::rejectWithDOMException(
+      scriptState,
+      DOMException::create(NotSupportedError, "Not implemented yet."));
+}
+
+DEFINE_TRACE(BackgroundFetchManager) {
+  visitor->trace(m_registration);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h
new file mode 100644
index 0000000..67d1819
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BackgroundFetchManager_h
+#define BackgroundFetchManager_h
+
+#include "bindings/core/v8/ScriptPromise.h"
+#include "bindings/core/v8/ScriptWrappable.h"
+#include "platform/heap/GarbageCollected.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class ScriptState;
+class ServiceWorkerRegistration;
+
+// Implementation of the BackgroundFetchManager JavaScript object, accessible
+// by developers through ServiceWorkerRegistration.backgroundFetch.
+class BackgroundFetchManager final
+    : public GarbageCollected<BackgroundFetchManager>,
+      public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static BackgroundFetchManager* create(
+      ServiceWorkerRegistration* registration) {
+    return new BackgroundFetchManager(registration);
+  }
+
+  ScriptPromise getTags(ScriptState*);
+
+  DECLARE_TRACE();
+
+ private:
+  explicit BackgroundFetchManager(ServiceWorkerRegistration*);
+
+  Member<ServiceWorkerRegistration> m_registration;
+};
+
+}  // namespace blink
+
+#endif  // BackgroundFetchManager_h
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl
new file mode 100644
index 0000000..18b02a80
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl
@@ -0,0 +1,15 @@
+// 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.
+
+// https://wicg.github.io/background-fetch/#background-fetch-manager
+
+[
+    Exposed=(Window,Worker),
+    RuntimeEnabled=BackgroundFetch
+] interface BackgroundFetchManager {
+    // TODO(peter): Implement the `fetch` method.
+    // TODO(peter): Implement the `get` method.
+
+    [CallWith=ScriptState] Promise<FrozenArray<DOMString>> getTags();
+};
diff --git a/third_party/WebKit/Source/modules/background_fetch/OWNERS b/third_party/WebKit/Source/modules/background_fetch/OWNERS
new file mode 100644
index 0000000..569df4e
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/OWNERS
@@ -0,0 +1,4 @@
+peter@chromium.org
+
+# TEAM: platform-capabilities@chromium.org
+# COMPONENT: Blink>BackgroundFetch
diff --git a/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.cpp b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.cpp
new file mode 100644
index 0000000..f2ae0f1
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.cpp
@@ -0,0 +1,60 @@
+// 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 "modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.h"
+
+#include "modules/background_fetch/BackgroundFetchManager.h"
+
+namespace blink {
+
+ServiceWorkerRegistrationBackgroundFetch::
+    ServiceWorkerRegistrationBackgroundFetch(
+        ServiceWorkerRegistration* registration)
+    : m_registration(registration) {}
+
+ServiceWorkerRegistrationBackgroundFetch::
+    ~ServiceWorkerRegistrationBackgroundFetch() = default;
+
+const char* ServiceWorkerRegistrationBackgroundFetch::supplementName() {
+  return "ServiceWorkerRegistrationBackgroundFetch";
+}
+
+ServiceWorkerRegistrationBackgroundFetch&
+ServiceWorkerRegistrationBackgroundFetch::from(
+    ServiceWorkerRegistration& registration) {
+  ServiceWorkerRegistrationBackgroundFetch* supplement =
+      static_cast<ServiceWorkerRegistrationBackgroundFetch*>(
+          Supplement<ServiceWorkerRegistration>::from(registration,
+                                                      supplementName()));
+
+  if (!supplement) {
+    supplement = new ServiceWorkerRegistrationBackgroundFetch(&registration);
+    provideTo(registration, supplementName(), supplement);
+  }
+
+  return *supplement;
+}
+
+BackgroundFetchManager*
+ServiceWorkerRegistrationBackgroundFetch::backgroundFetch(
+    ServiceWorkerRegistration& registration) {
+  return ServiceWorkerRegistrationBackgroundFetch::from(registration)
+      .backgroundFetch();
+}
+
+BackgroundFetchManager*
+ServiceWorkerRegistrationBackgroundFetch::backgroundFetch() {
+  if (!m_backgroundFetchManager)
+    m_backgroundFetchManager = BackgroundFetchManager::create(m_registration);
+
+  return m_backgroundFetchManager.get();
+}
+
+DEFINE_TRACE(ServiceWorkerRegistrationBackgroundFetch) {
+  visitor->trace(m_registration);
+  visitor->trace(m_backgroundFetchManager);
+  Supplement<ServiceWorkerRegistration>::trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.h b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.h
new file mode 100644
index 0000000..98d7aeb
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ServiceWorkerRegistrationBackgroundFetch_h
+#define ServiceWorkerRegistrationBackgroundFetch_h
+
+#include "modules/serviceworkers/ServiceWorkerRegistration.h"
+#include "platform/Supplementable.h"
+#include "platform/heap/GarbageCollected.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class BackgroundFetchManager;
+
+class ServiceWorkerRegistrationBackgroundFetch final
+    : public GarbageCollectedFinalized<
+          ServiceWorkerRegistrationBackgroundFetch>,
+      public Supplement<ServiceWorkerRegistration> {
+  USING_GARBAGE_COLLECTED_MIXIN(ServiceWorkerRegistrationBackgroundFetch);
+  WTF_MAKE_NONCOPYABLE(ServiceWorkerRegistrationBackgroundFetch);
+
+ public:
+  virtual ~ServiceWorkerRegistrationBackgroundFetch();
+
+  static ServiceWorkerRegistrationBackgroundFetch& from(
+      ServiceWorkerRegistration&);
+
+  static BackgroundFetchManager* backgroundFetch(ServiceWorkerRegistration&);
+  BackgroundFetchManager* backgroundFetch();
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  explicit ServiceWorkerRegistrationBackgroundFetch(ServiceWorkerRegistration*);
+  static const char* supplementName();
+
+  Member<ServiceWorkerRegistration> m_registration;
+  Member<BackgroundFetchManager> m_backgroundFetchManager;
+};
+
+}  // namespace blink
+
+#endif  // ServiceWorkerRegistrationBackgroundFetch_h
diff --git a/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.idl b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.idl
new file mode 100644
index 0000000..9d0ffdc
--- /dev/null
+++ b/third_party/WebKit/Source/modules/background_fetch/ServiceWorkerRegistrationBackgroundFetch.idl
@@ -0,0 +1,12 @@
+// 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.
+
+// https://wicg.github.io/background-fetch/#extensions-to-service-worker-registration
+
+[
+    Exposed=(Window,Worker),
+    RuntimeEnabled=BackgroundFetch
+] partial interface ServiceWorkerRegistration {
+    readonly attribute BackgroundFetchManager backgroundFetch;
+};
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 934f168..4880c79 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -41,6 +41,7 @@
     get_path_info([
                     "app_banner/AppBannerPromptResult.idl",
                     "app_banner/BeforeInstallPromptEvent.idl",
+                    "background_fetch/BackgroundFetchManager.idl",
                     "background_sync/SyncEvent.idl",
                     "background_sync/SyncManager.idl",
                     "battery/BatteryManager.idl",
@@ -555,6 +556,7 @@
     get_path_info(
         [
           "audio_output_devices/HTMLMediaElementAudioOutputDevice.idl",
+          "background_fetch/ServiceWorkerRegistrationBackgroundFetch.idl",
           "background_sync/ServiceWorkerGlobalScopeSync.idl",
           "background_sync/ServiceWorkerRegistrationSync.idl",
           "battery/NavigatorBattery.idl",
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 83b46fe..c582489 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -86,6 +86,10 @@
       status: "stable",
     },
     {
+      name: "BackgroundFetch",
+      status: "test",
+    },
+    {
       name: "BackgroundSync",
       status: "stable",
     },
diff --git a/third_party/WebKit/Source/platform/exported/WebURLRequest.cpp b/third_party/WebKit/Source/platform/exported/WebURLRequest.cpp
index 893a4163..88b0ea0 100644
--- a/third_party/WebKit/Source/platform/exported/WebURLRequest.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebURLRequest.cpp
@@ -364,7 +364,8 @@
 }
 
 void WebURLRequest::setExtraData(WebURLRequest::ExtraData* extraData) {
-  m_resourceRequest->setExtraData(ExtraDataContainer::create(extraData));
+  if (extraData != getExtraData())
+    m_resourceRequest->setExtraData(ExtraDataContainer::create(extraData));
 }
 
 ResourceRequest& WebURLRequest::toMutableResourceRequest() {
diff --git a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
index 92e913cc..852323a 100644
--- a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
@@ -369,7 +369,8 @@
 }
 
 void WebURLResponse::setExtraData(WebURLResponse::ExtraData* extraData) {
-  m_resourceResponse->setExtraData(ExtraDataContainer::create(extraData));
+  if (extraData != getExtraData())
+    m_resourceResponse->setExtraData(ExtraDataContainer::create(extraData));
 }
 
 void WebURLResponse::appendRedirectResponse(const WebURLResponse& response) {
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 73c1b65e..31528fbc 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -946,19 +946,6 @@
   return WTF::wrapUnique(m_webFrame->client()->createServiceWorkerProvider());
 }
 
-bool LocalFrameClientImpl::isControlledByServiceWorker(DocumentLoader& loader) {
-  return m_webFrame->client() &&
-         m_webFrame->client()->isControlledByServiceWorker(
-             *WebDataSourceImpl::fromDocumentLoader(&loader));
-}
-
-int64_t LocalFrameClientImpl::serviceWorkerID(DocumentLoader& loader) {
-  if (!m_webFrame->client())
-    return -1;
-  return m_webFrame->client()->serviceWorkerID(
-      *WebDataSourceImpl::fromDocumentLoader(&loader));
-}
-
 SharedWorkerRepositoryClient*
 LocalFrameClientImpl::sharedWorkerRepositoryClient() {
   return m_webFrame->sharedWorkerRepositoryClient();
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.h b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
index 203a6cb..f5fbad33 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
@@ -199,8 +199,6 @@
 
   std::unique_ptr<WebServiceWorkerProvider> createServiceWorkerProvider()
       override;
-  bool isControlledByServiceWorker(DocumentLoader&) override;
-  int64_t serviceWorkerID(DocumentLoader&) override;
   SharedWorkerRepositoryClient* sharedWorkerRepositoryClient() override;
 
   std::unique_ptr<WebApplicationCacheHost> createApplicationCacheHost(
diff --git a/third_party/WebKit/Source/web/WebDataSourceImpl.cpp b/third_party/WebKit/Source/web/WebDataSourceImpl.cpp
index c89c9bf..d8a3caf 100644
--- a/third_party/WebKit/Source/web/WebDataSourceImpl.cpp
+++ b/third_party/WebKit/Source/web/WebDataSourceImpl.cpp
@@ -37,6 +37,7 @@
 #include "public/platform/WebURL.h"
 #include "public/platform/WebURLError.h"
 #include "public/platform/WebVector.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "wtf/PtrUtil.h"
 
 namespace blink {
@@ -161,6 +162,16 @@
       SubresourceFilter::create(this, WTF::wrapUnique(subresourceFilter)));
 }
 
+void WebDataSourceImpl::setServiceWorkerNetworkProvider(
+    std::unique_ptr<WebServiceWorkerNetworkProvider> provider) {
+  DocumentLoader::setServiceWorkerNetworkProvider(std::move(provider));
+}
+
+WebServiceWorkerNetworkProvider*
+WebDataSourceImpl::getServiceWorkerNetworkProvider() {
+  return DocumentLoader::getServiceWorkerNetworkProvider();
+}
+
 DEFINE_TRACE(WebDataSourceImpl) {
   DocumentLoader::trace(visitor);
 }
diff --git a/third_party/WebKit/Source/web/WebDataSourceImpl.h b/third_party/WebKit/Source/web/WebDataSourceImpl.h
index f0f464d..f09ccb0 100644
--- a/third_party/WebKit/Source/web/WebDataSourceImpl.h
+++ b/third_party/WebKit/Source/web/WebDataSourceImpl.h
@@ -73,6 +73,9 @@
                         double fetchStartTime,
                         bool hasRedirect) override;
   void setSubresourceFilter(WebDocumentSubresourceFilter*) override;
+  void setServiceWorkerNetworkProvider(
+      std::unique_ptr<WebServiceWorkerNetworkProvider>) override;
+  WebServiceWorkerNetworkProvider* getServiceWorkerNetworkProvider() override;
 
   static WebNavigationType toWebNavigationType(NavigationType);
 
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
index 3135c41..7ed9670 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
@@ -59,6 +59,7 @@
 #include "platform/weborigin/SecurityOrigin.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebURLRequest.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerProvider.h"
 #include "public/web/WebConsoleMessage.h"
 #include "public/web/WebDevToolsAgent.h"
@@ -66,7 +67,6 @@
 #include "public/web/WebView.h"
 #include "public/web/WebWorkerContentSettingsClientProxy.h"
 #include "public/web/modules/serviceworker/WebServiceWorkerContextClient.h"
-#include "public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "web/IndexedDBClientImpl.h"
 #include "web/ServiceWorkerGlobalScopeClientImpl.h"
 #include "web/ServiceWorkerGlobalScopeProxy.h"
@@ -333,21 +333,21 @@
 
 void WebEmbeddedWorkerImpl::willSendRequest(WebLocalFrame* frame,
                                             WebURLRequest& request) {
-  if (m_networkProvider)
-    m_networkProvider->willSendRequest(frame->dataSource(), request);
+  auto* networkProvider =
+      frame->dataSource()->getServiceWorkerNetworkProvider();
+  if (networkProvider)
+    networkProvider->willSendRequest(request);
 }
 
 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame) {
   DCHECK(!m_mainScriptLoader);
-  DCHECK(!m_networkProvider);
   DCHECK(m_mainFrame);
   DCHECK(m_workerContextClient);
   DCHECK(m_loadingShadowPage);
   DCHECK(!m_askedToTerminate);
   m_loadingShadowPage = false;
-  m_networkProvider =
-      WTF::wrapUnique(m_workerContextClient->createServiceWorkerNetworkProvider(
-          frame->dataSource()));
+  frame->dataSource()->setServiceWorkerNetworkProvider(WTF::wrapUnique(
+      m_workerContextClient->createServiceWorkerNetworkProvider()));
   m_mainScriptLoader = WorkerScriptLoader::create();
   m_mainScriptLoader->setRequestContext(
       WebURLRequest::RequestContextServiceWorker);
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
index d98cd69..0c49784 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
@@ -47,7 +47,6 @@
 class ParentFrameTaskRunners;
 class ServiceWorkerGlobalScopeProxy;
 class WebLocalFrameImpl;
-class WebServiceWorkerNetworkProvider;
 class WebView;
 class WorkerInspectorProxy;
 class WorkerScriptLoader;
@@ -117,10 +116,6 @@
   // to WorkerContext.
   std::unique_ptr<WebWorkerContentSettingsClientProxy> m_contentSettingsClient;
 
-  // We retain ownership of this one which is for use on the
-  // main thread only.
-  std::unique_ptr<WebServiceWorkerNetworkProvider> m_networkProvider;
-
   // Kept around only while main script loading is ongoing.
   RefPtr<WorkerScriptLoader> m_mainScriptLoader;
 
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp b/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp
index e7aa6ec..6f5937b 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp
@@ -28,8 +28,8 @@
   MOCK_METHOD0(workerReadyForInspection, void());
   MOCK_METHOD0(workerContextFailedToStart, void());
   MOCK_METHOD0(workerScriptLoaded, void());
-  MOCK_METHOD1(createServiceWorkerNetworkProvider,
-               WebServiceWorkerNetworkProvider*(WebDataSource*));
+  MOCK_METHOD0(createServiceWorkerNetworkProvider,
+               WebServiceWorkerNetworkProvider*());
   MOCK_METHOD0(createServiceWorkerProvider, WebServiceWorkerProvider*());
   bool hasAssociatedRegistration() override {
     return m_hasAssociatedRegistration;
@@ -143,7 +143,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
@@ -162,7 +162,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
@@ -198,7 +198,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
@@ -219,7 +219,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
@@ -246,7 +246,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
@@ -274,7 +274,7 @@
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
 
   // Load the shadow page.
-  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider(::testing::_))
+  EXPECT_CALL(*m_mockClient, createServiceWorkerNetworkProvider())
       .WillOnce(::testing::Return(nullptr));
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
   ::testing::Mock::VerifyAndClearExpectations(m_mockClient);
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
index 1ffd05d..0a5ad09 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
@@ -59,12 +59,12 @@
 #include "public/platform/WebString.h"
 #include "public/platform/WebURL.h"
 #include "public/platform/WebURLRequest.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "public/web/WebDevToolsAgent.h"
 #include "public/web/WebFrame.h"
 #include "public/web/WebSettings.h"
 #include "public/web/WebView.h"
 #include "public/web/WebWorkerContentSettingsClientProxy.h"
-#include "public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h"
 #include "web/IndexedDBClientImpl.h"
 #include "web/LocalFileSystemClient.h"
 #include "web/WebDataSourceImpl.h"
@@ -170,17 +170,18 @@
 
 void WebSharedWorkerImpl::willSendRequest(WebLocalFrame* frame,
                                           WebURLRequest& request) {
-  DCHECK(isMainThread());
-  if (m_networkProvider)
-    m_networkProvider->willSendRequest(frame->dataSource(), request);
+  auto* networkProvider =
+      frame->dataSource()->getServiceWorkerNetworkProvider();
+  if (networkProvider)
+    networkProvider->willSendRequest(request);
 }
 
 void WebSharedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame) {
   DCHECK(isMainThread());
   DCHECK(!m_loadingDocument);
   DCHECK(!m_mainScriptLoader);
-  m_networkProvider = WTF::wrapUnique(
-      m_client->createServiceWorkerNetworkProvider(frame->dataSource()));
+  frame->dataSource()->setServiceWorkerNetworkProvider(
+      WTF::wrapUnique(m_client->createServiceWorkerNetworkProvider()));
   m_mainScriptLoader = WorkerScriptLoader::create();
   m_mainScriptLoader->setRequestContext(
       WebURLRequest::RequestContextSharedWorker);
@@ -201,20 +202,6 @@
   // invoked and |this| might have been deleted at this point.
 }
 
-bool WebSharedWorkerImpl::isControlledByServiceWorker(
-    WebDataSource& dataSource) {
-  DCHECK(isMainThread());
-  return m_networkProvider &&
-         m_networkProvider->isControlledByServiceWorker(dataSource);
-}
-
-int64_t WebSharedWorkerImpl::serviceWorkerID(WebDataSource& dataSource) {
-  DCHECK(isMainThread());
-  if (!m_networkProvider)
-    return -1;
-  return m_networkProvider->serviceWorkerID(dataSource);
-}
-
 void WebSharedWorkerImpl::sendProtocolMessage(int sessionId,
                                               int callId,
                                               const WebString& message,
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
index 7454b51..c6b6ef1 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
@@ -75,8 +75,6 @@
       WebApplicationCacheHostClient*) override;
   void willSendRequest(WebLocalFrame*, WebURLRequest&) override;
   void didFinishDocumentLoad(WebLocalFrame*) override;
-  bool isControlledByServiceWorker(WebDataSource&) override;
-  int64_t serviceWorkerID(WebDataSource&) override;
 
   // WebDevToolsAgentClient overrides.
   void sendProtocolMessage(int sessionId,
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index e84d0ea..a2cfbe0 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -384,6 +384,7 @@
     "platform/modules/serviceworker/WebServiceWorkerClientsInfo.h",
     "platform/modules/serviceworker/WebServiceWorkerError.h",
     "platform/modules/serviceworker/WebServiceWorkerEventResult.h",
+    "platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h",
     "platform/modules/serviceworker/WebServiceWorkerProvider.h",
     "platform/modules/serviceworker/WebServiceWorkerProviderClient.h",
     "platform/modules/serviceworker/WebServiceWorkerProxy.h",
@@ -572,7 +573,6 @@
     "web/mac/WebSubstringUtil.h",
     "web/modules/serviceworker/WebServiceWorkerContextClient.h",
     "web/modules/serviceworker/WebServiceWorkerContextProxy.h",
-    "web/modules/serviceworker/WebServiceWorkerNetworkProvider.h",
     "web/win/WebFontRendering.h",
   ]
 
diff --git a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
similarity index 90%
rename from third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h
rename to third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
index 7e5c111..b17563e9 100644
--- a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
@@ -33,13 +33,15 @@
 
 namespace blink {
 
-class WebDataSource;
 class WebURLRequest;
 
 // This interface is implemented by the client and is only called on the main
 // thread. Used by ServiceWorker and SharedWorker. isControlledByServiceWorker()
 // and serviceWorkerID() are to be implemented only by SharedWorker's provider,
 // as they are needed only for controllee workers.
+//
+// An instance of this class is owned by the associated loading context, e.g.
+// DocumentLoader.
 class WebServiceWorkerNetworkProvider {
  public:
   virtual ~WebServiceWorkerNetworkProvider() {}
@@ -47,15 +49,15 @@
   // A request is about to be sent out, and the client may modify it. Request
   // is writable, and changes to the URL, for example, will change the request
   // made.
-  virtual void willSendRequest(WebDataSource*, WebURLRequest&) {}
+  virtual void willSendRequest(WebURLRequest&) {}
 
   // Whether the document associated with WebDataSource is controlled by the
   // ServiceWorker.
-  virtual bool isControlledByServiceWorker(WebDataSource&) { return false; }
+  virtual bool isControlledByServiceWorker() { return false; }
 
   // Returns an identifier of the service worker controlling the document
   // associated with the WebDataSource.
-  virtual int64_t serviceWorkerID(WebDataSource&) { return -1; }
+  virtual int64_t serviceWorkerID() { return -1; }
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/web/WebDataSource.h b/third_party/WebKit/public/web/WebDataSource.h
index c2b118a..a084487 100644
--- a/third_party/WebKit/public/web/WebDataSource.h
+++ b/third_party/WebKit/public/web/WebDataSource.h
@@ -31,6 +31,8 @@
 #ifndef WebDataSource_h
 #define WebDataSource_h
 
+#include <memory>
+
 #include "../platform/WebCommon.h"
 #include "WebNavigationType.h"
 #include "WebTextDirection.h"
@@ -38,6 +40,7 @@
 namespace blink {
 
 class WebDocumentSubresourceFilter;
+class WebServiceWorkerNetworkProvider;
 class WebURL;
 class WebURLRequest;
 class WebURLResponse;
@@ -120,6 +123,14 @@
   // datasource is destroyed or when a new filter is set.
   virtual void setSubresourceFilter(WebDocumentSubresourceFilter*) = 0;
 
+  // Allows the embedder to set and return the service worker provider
+  // associated with the data source. The provider may provide the service
+  // worker that controls the resource loading from this data source.
+  virtual void setServiceWorkerNetworkProvider(
+      std::unique_ptr<WebServiceWorkerNetworkProvider>) = 0;
+  virtual WebServiceWorkerNetworkProvider*
+  getServiceWorkerNetworkProvider() = 0;
+
  protected:
   ~WebDataSource() {}
 };
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 5bf558bd..539f8ef 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -679,16 +679,6 @@
       const WebAXObject& endObject,
       int endOffset) {}
 
-  // ServiceWorker -------------------------------------------------------
-
-  // Whether the document associated with WebDataSource is controlled by the
-  // ServiceWorker.
-  virtual bool isControlledByServiceWorker(WebDataSource&) { return false; }
-
-  // Returns an identifier of the service worker controlling the document
-  // associated with the WebDataSource.
-  virtual int64_t serviceWorkerID(WebDataSource&) { return -1; }
-
   // Fullscreen ----------------------------------------------------------
 
   // Called to enter/exit fullscreen mode.
diff --git a/third_party/WebKit/public/web/WebSharedWorkerClient.h b/third_party/WebKit/public/web/WebSharedWorkerClient.h
index e75a34c5..0525e891c 100644
--- a/third_party/WebKit/public/web/WebSharedWorkerClient.h
+++ b/third_party/WebKit/public/web/WebSharedWorkerClient.h
@@ -38,7 +38,6 @@
 
 class WebApplicationCacheHost;
 class WebApplicationCacheHostClient;
-class WebDataSource;
 class WebNotificationPresenter;
 class WebSecurityOrigin;
 class WebServiceWorkerNetworkProvider;
@@ -81,8 +80,8 @@
 
   // Called on the main thread during initialization.
   // Ownership of the returned object is transferred to the caller.
-  virtual WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider(
-      WebDataSource*) {
+  virtual WebServiceWorkerNetworkProvider*
+  createServiceWorkerNetworkProvider() {
     return nullptr;
   }
 
diff --git a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
index 4d1105a..c272ec9b 100644
--- a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
+++ b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
@@ -45,7 +45,6 @@
 namespace blink {
 
 struct WebServiceWorkerClientQueryOptions;
-class WebDataSource;
 class WebServiceWorkerContextProxy;
 class WebServiceWorkerNetworkProvider;
 class WebServiceWorkerProvider;
@@ -203,8 +202,8 @@
 
   // Ownership of the returned object is transferred to the caller.
   // This is called on the main thread.
-  virtual WebServiceWorkerNetworkProvider* createServiceWorkerNetworkProvider(
-      WebDataSource*) {
+  virtual WebServiceWorkerNetworkProvider*
+  createServiceWorkerNetworkProvider() {
     return nullptr;
   }
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8c7f49e..a4e22d5a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -57428,7 +57428,15 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.Pref.Scout" enum="BooleanEnabled">
+<histogram name="SafeBrowsing.Pref.SawInterstitial" enum="Boolean">
+  <owner>lpz@chromium.org</owner>
+  <summary>
+    Whether the current user has ever seen a security interstitial. Recorded for
+    all non-Incognito profiles on profile startup.
+  </summary>
+</histogram>
+
+<histogram name="SafeBrowsing.Pref.Scout" enum="NullableBoolean">
   <owner>lpz@chromium.org</owner>
   <summary>
     Tracks the Extended Reporting preference transition. Suffixes track which
@@ -102883,6 +102891,15 @@
   <int value="0" label="0x00000000 - STATUS_SUCCESS"/>
 </enum>
 
+<enum name="NullableBoolean" type="int">
+  <summary>
+    A Nullable Boolean can True, False or Null (ie: unset or absent).
+  </summary>
+  <int value="0" label="False"/>
+  <int value="1" label="True"/>
+  <int value="2" label="Null"/>
+</enum>
+
 <enum name="OAuth2LoginAccountRevokedMigrationState" type="int">
   <int value="0" label="Account ID migration not started"/>
   <int value="1" label="Account ID migration in progress"/>
@@ -121290,6 +121307,7 @@
 <histogram_suffixes name="SafeBrowsingScoutPrefValues" separator=".">
   <suffix name="SBER1Pref" label="Value of the legacy Extended Reporting pref"/>
   <suffix name="SBER2Pref" label="Value of the new Extended Reporting pref"/>
+  <affected-histogram name="SafeBrowsing.Pref.SawInterstitial"/>
   <affected-histogram name="SafeBrowsing.Pref.Scout.NoScoutGroup"/>
   <affected-histogram name="SafeBrowsing.Pref.Scout.ScoutGroup"/>
   <affected-histogram name="SafeBrowsing.Pref.Scout.SetPref"/>
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py
index 72a0d76..977f473 100644
--- a/tools/perf/benchmarks/v8_browsing.py
+++ b/tools/perf/benchmarks/v8_browsing.py
@@ -167,6 +167,7 @@
   TEST_SUFFIX = ''
 
 
+@benchmark.Disabled('reference')  # http://crbug.com/700390
 class V8DesktopTurboBrowsingBenchmark(_V8DesktopBrowsingBenchmark):
   PLATFORM = 'desktop'
   TEST_SUFFIX = '_turbo'
@@ -197,6 +198,7 @@
     return possible_browser.platform.GetDeviceTypeName() != 'Desktop'
 
 
+@benchmark.Disabled('reference')  # http://crbug.com/700390
 class V8RuntimeStatsDesktopTurboBrowsingBenchmark(
     _V8RuntimeStatsBrowsingBenchmark):
   PLATFORM = 'desktop'
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
index 8e3dedac..bf674594 100644
--- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
+++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
@@ -4,7 +4,6 @@
 
 package org.chromium.ui.base;
 
-import android.annotation.TargetApi;
 import android.content.ClipData;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -27,6 +26,10 @@
 @JNINamespace("ui")
 public abstract class ViewAndroidDelegate {
     private static final String TAG = "ViewAndroidDelegate";
+
+    // TODO(hush): use View#DRAG_FLAG_GLOBAL when Chromium starts to build with API 24.
+    private static final int DRAG_FLAG_GLOBAL = 1 << 8;
+
     private static final String GEO_SCHEME = "geo";
     private static final String TEL_SCHEME = "tel";
     private static final String MAILTO_SCHEME = "mailto";
@@ -92,7 +95,8 @@
      * @param shadowImage The shadow image for the dragged text.
      */
     @SuppressWarnings("deprecation")
-    @TargetApi(Build.VERSION_CODES.N)
+    // TODO(hush): uncomment below when we build with API 24.
+    // @TargetApi(Build.VERSION_CODES.N)
     @CalledByNative
     private boolean startDragAndDrop(String text, Bitmap shadowImage) {
         if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) return false;
@@ -104,8 +108,9 @@
         imageView.setImageBitmap(shadowImage);
         imageView.layout(0, 0, shadowImage.getWidth(), shadowImage.getHeight());
 
-        return containerView.startDragAndDrop(ClipData.newPlainText(null, text),
-                new View.DragShadowBuilder(imageView), null, View.DRAG_FLAG_GLOBAL);
+        // TODO(hush): use View#startDragAndDrop when Chromium starts to build with API 24.
+        return containerView.startDrag(ClipData.newPlainText(null, text),
+                new View.DragShadowBuilder(imageView), null, DRAG_FLAG_GLOBAL);
     }
 
     /**
diff --git a/ui/events/platform/x11/x11_event_source.cc b/ui/events/platform/x11/x11_event_source.cc
index 9fbfe0d..f8bbaeb 100644
--- a/ui/events/platform/x11/x11_event_source.cc
+++ b/ui/events/platform/x11/x11_event_source.cc
@@ -149,10 +149,6 @@
   BlockOnWindowStructureEvent(window, MapNotify);
 }
 
-void X11EventSource::BlockUntilWindowUnmapped(XID window) {
-  BlockOnWindowStructureEvent(window, UnmapNotify);
-}
-
 Time X11EventSource::GetCurrentServerTime() {
   DCHECK(display_);
 
diff --git a/ui/events/platform/x11/x11_event_source.h b/ui/events/platform/x11/x11_event_source.h
index 7b1c37d..c274490 100644
--- a/ui/events/platform/x11/x11_event_source.h
+++ b/ui/events/platform/x11/x11_event_source.h
@@ -73,8 +73,6 @@
   // functions which require a mapped window.
   void BlockUntilWindowMapped(XID window);
 
-  void BlockUntilWindowUnmapped(XID window);
-
   XDisplay* display() { return display_; }
 
   // Returns the timestamp of the event currently being dispatched.  Falls back
diff --git a/ui/views/bubble/bubble_dialog_delegate.cc b/ui/views/bubble/bubble_dialog_delegate.cc
index 735a2237..c32f4c3 100644
--- a/ui/views/bubble/bubble_dialog_delegate.cc
+++ b/ui/views/bubble/bubble_dialog_delegate.cc
@@ -216,12 +216,9 @@
       adjust_if_offscreen_(true),
       parent_window_(NULL) {
   ViewsDelegate* views_delegate = ViewsDelegate::GetInstance();
-  margins_ = views_delegate ? views_delegate->GetBubbleDialogMargins()
-                            : gfx::Insets(kPanelVertMargin, kPanelHorizMargin);
-  title_margins_ = views_delegate
-                       ? views_delegate->GetDialogFrameViewInsets()
-                       : gfx::Insets(kPanelVertMargin, kPanelHorizMargin, 0,
-                                     kPanelHorizMargin);
+  margins_ = views_delegate->GetInsetsMetric(InsetsMetric::BUBBLE_DIALOG);
+  title_margins_ =
+      views_delegate->GetInsetsMetric(InsetsMetric::DIALOG_FRAME_VIEW);
   if (anchor_view)
     SetAnchorView(anchor_view);
   UpdateColorsFromTheme(GetNativeTheme());
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 14f5622..a8220bc 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -314,8 +314,8 @@
     return;
 
   // The close button is positioned somewhat closer to the edge of the bubble.
-  const int close_margin =
-      ViewsDelegate::GetInstance()->GetDialogCloseButtonMargin();
+  const int close_margin = ViewsDelegate::GetInstance()->GetDistanceMetric(
+      DistanceMetric::CLOSE_BUTTON_MARGIN);
   close_->SetPosition(
       gfx::Point(contents_bounds.right() - close_margin - close_->width(),
                  contents_bounds.y() + close_margin));
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index 4a7cfd9..028c4710 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -25,15 +25,14 @@
 #include "ui/views/animation/square_ink_drop_ripple.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button_border.h"
+#include "ui/views/layout/layout_constants.h"
 #include "ui/views/painter.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/views_delegate.h"
 #include "ui/views/window/dialog_delegate.h"
 
 namespace {
 
-// The default spacing between the icon and text.
-const int kSpacing = 5;
-
 gfx::Font::Weight GetValueBolderThan(gfx::Font::Weight weight) {
   if (weight < gfx::Font::Weight::BOLD)
     return gfx::Font::Weight::BOLD;
@@ -93,7 +92,8 @@
       is_default_(false),
       style_(STYLE_TEXTBUTTON),
       border_is_themed_border_(true),
-      image_label_spacing_(kSpacing),
+      image_label_spacing_(ViewsDelegate::GetInstance()->GetDistanceMetric(
+          DistanceMetric::RELATED_CONTROL_HORIZONTAL)),
       horizontal_alignment_(gfx::ALIGN_LEFT) {
   SetAnimationDuration(kHoverAnimationDurationMs);
   SetTextInternal(text);
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc
index b5e6c1b..4453f2a 100644
--- a/ui/views/controls/button/md_text_button.cc
+++ b/ui/views/controls/button/md_text_button.cc
@@ -193,8 +193,8 @@
   set_has_ink_drop_action_on_click(true);
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
   SetFocusForPlatform();
-  const int minimum_width =
-      ViewsDelegate::GetInstance()->GetButtonMinimumWidth();
+  const int minimum_width = ViewsDelegate::GetInstance()->GetDistanceMetric(
+      DistanceMetric::DIALOG_BUTTON_MINIMUM_WIDTH);
   SetMinSize(gfx::Size(minimum_width, 0));
   SetFocusPainter(nullptr);
   label()->SetAutoColorReadabilityEnabled(false);
@@ -241,7 +241,8 @@
   // TODO(estade): can we get rid of the platform style border hoopla if
   // we apply the MD treatment to all buttons, even GTK buttons?
   const int horizontal_padding =
-      ViewsDelegate::GetInstance()->GetButtonHorizontalPadding();
+      ViewsDelegate::GetInstance()->GetDistanceMetric(
+          DistanceMetric::BUTTON_HORIZONTAL_PADDING);
   SetBorder(CreateEmptyBorder(top_padding, horizontal_padding, bottom_padding,
                               horizontal_padding));
 }
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index cadbfa4..24c5170 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -131,8 +131,11 @@
   ~TransparentButton() override {}
 
   bool OnMousePressed(const ui::MouseEvent& mouse_event) override {
-    if (!UseMd())
-      parent()->RequestFocus();
+#if !defined(OS_MACOSX)
+    // On Mac, comboboxes do not take focus on mouse click, but on other
+    // platforms they do.
+    parent()->RequestFocus();
+#endif
     return CustomButton::OnMousePressed(mouse_event);
   }
 
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index 37e8770..56be1b8 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -132,7 +132,8 @@
   const float kStretchy = 1.f;
 
   const int horizontal_spacing =
-      ViewsDelegate::GetInstance()->GetDialogRelatedButtonHorizontalSpacing();
+      ViewsDelegate::GetInstance()->GetDistanceMetric(
+          views::DistanceMetric::RELATED_BUTTON_HORIZONTAL);
   GridLayout* layout = GridLayout::CreatePanel(container);
   container->SetLayoutManager(layout);
   ColumnSet* column_set = layout->AddColumnSet(kFieldsColumnId);
@@ -187,7 +188,8 @@
   const float kFixedVerticalResize = 0.f;
   layout->StartRowWithPadding(
       kFixedVerticalResize, kFieldsColumnId, kFixedVerticalResize,
-      ViewsDelegate::GetInstance()->GetDialogRelatedControlVerticalSpacing());
+      ViewsDelegate::GetInstance()->GetDistanceMetric(
+          views::DistanceMetric::RELATED_CONTROL_VERTICAL));
   layout->AddView(new Label(base::ASCIIToUTF16(label)));
 }
 
diff --git a/ui/views/layout/layout_constants.h b/ui/views/layout/layout_constants.h
index 620a28d..9813351 100644
--- a/ui/views/layout/layout_constants.h
+++ b/ui/views/layout/layout_constants.h
@@ -71,7 +71,7 @@
 constexpr int kButtonHEdgeMarginNew = 20;
 
 // Spacing between the edge of the window and the edge of the close button.
-const int kCloseButtonMargin = 7;
+constexpr int kCloseButtonMargin = 7;
 
 // Horizontal spacing between buttons that are logically related.
 constexpr int kRelatedButtonHSpacing = 6;
@@ -90,7 +90,7 @@
 const int kMinimumButtonWidth = 48;
 
 // Minimum width of dialog buttons.
-const int kDialogMinimumButtonWidth = 75;
+constexpr int kDialogMinimumButtonWidth = 75;
 
 }  // namespace views
 
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index 8a78a96..a14cf488 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -126,42 +126,38 @@
   return nullptr;
 }
 
-gfx::Insets ViewsDelegate::GetDialogButtonInsets() const {
-  return gfx::Insets(0, kButtonHEdgeMarginNew, kButtonVEdgeMarginNew,
-                     kButtonHEdgeMarginNew);
+gfx::Insets ViewsDelegate::GetInsetsMetric(InsetsMetric metric) const {
+  switch (metric) {
+    case InsetsMetric::DIALOG_BUTTON:
+      return gfx::Insets(0, kButtonHEdgeMarginNew, kButtonVEdgeMarginNew,
+                         kButtonHEdgeMarginNew);
+    case InsetsMetric::DIALOG_FRAME_VIEW:
+      return gfx::Insets(kPanelVertMargin, kButtonHEdgeMarginNew, 0,
+                         kButtonHEdgeMarginNew);
+    case InsetsMetric::BUBBLE_DIALOG:
+      return gfx::Insets(kPanelVertMargin, kPanelHorizMargin);
+  }
+  NOTREACHED();
+  return gfx::Insets();
 }
 
-int ViewsDelegate::GetDialogCloseButtonMargin() const {
-  return kCloseButtonMargin;
-}
-
-int ViewsDelegate::GetDialogRelatedButtonHorizontalSpacing() const {
-  return kRelatedButtonHSpacing;
-}
-
-int ViewsDelegate::GetDialogRelatedControlVerticalSpacing() const {
-  return kRelatedControlVerticalSpacing;
-}
-
-gfx::Insets ViewsDelegate::GetDialogFrameViewInsets() const {
-  return gfx::Insets(kPanelVertMargin, kButtonHEdgeMarginNew, 0,
-                     kButtonHEdgeMarginNew);
-}
-
-gfx::Insets ViewsDelegate::GetBubbleDialogMargins() const {
-  return gfx::Insets(kPanelVertMargin, kPanelHorizMargin);
-}
-
-int ViewsDelegate::GetButtonMinimumWidth() const {
-  return kMinimumButtonWidth;
-}
-
-int ViewsDelegate::GetDialogButtonMinimumWidth() const {
-  return kDialogMinimumButtonWidth;
-}
-
-int ViewsDelegate::GetButtonHorizontalPadding() const {
-  return kButtonHorizontalPadding;
+int ViewsDelegate::GetDistanceMetric(DistanceMetric metric) const {
+  switch (metric) {
+    case DistanceMetric::CLOSE_BUTTON_MARGIN:
+      return kCloseButtonMargin;
+    case DistanceMetric::RELATED_BUTTON_HORIZONTAL:
+      return kRelatedButtonHSpacing;
+    case DistanceMetric::RELATED_CONTROL_HORIZONTAL:
+      return kRelatedControlHorizontalSpacing;
+    case DistanceMetric::RELATED_CONTROL_VERTICAL:
+      return kRelatedControlVerticalSpacing;
+    case DistanceMetric::DIALOG_BUTTON_MINIMUM_WIDTH:
+      return kDialogMinimumButtonWidth;
+    case DistanceMetric::BUTTON_HORIZONTAL_PADDING:
+      return kButtonHorizontalPadding;
+  }
+  NOTREACHED();
+  return 0;
 }
 
 ViewsDelegate::ViewsDelegate()
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 7b3960f..6a784af5 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -58,6 +58,35 @@
 class NativeWidgetDelegate;
 }
 
+enum class InsetsMetric {
+  // The margins that should be applied around a bubble dialog.
+  BUBBLE_DIALOG,
+  // The insets that should be applied around a DialogClientView. Note that
+  // the top inset is used for the distance between the buttons and the
+  // DialogClientView's content view.
+  DIALOG_BUTTON,
+  // The insets that should be applied around a dialog's frame view.
+  DIALOG_FRAME_VIEW,
+};
+
+enum class DistanceMetric {
+  // The default padding to add on each side of a button's label.
+  BUTTON_HORIZONTAL_PADDING,
+  // The distance between a dialog's edge and the close button in the upper
+  // trailing corner.
+  CLOSE_BUTTON_MARGIN,
+  // The default minimum width of a dialog button.
+  DIALOG_BUTTON_MINIMUM_WIDTH,
+  // The spacing between a pair of related horizontal buttons, used for
+  // dialog layout.
+  RELATED_BUTTON_HORIZONTAL,
+  // Horizontal spacing between controls that are logically related.
+  RELATED_CONTROL_HORIZONTAL,
+  // The spacing between a pair of related vertical controls, used for
+  // dialog layout.
+  RELATED_CONTROL_VERTICAL,
+};
+
 // ViewsDelegate is an interface implemented by an object using the views
 // framework. It is used to obtain various high level application utilities
 // and perform some actions such as window placement saving.
@@ -196,37 +225,12 @@
   // Returns a blocking pool task runner given a TaskRunnerType.
   virtual scoped_refptr<base::TaskRunner> GetBlockingPoolTaskRunner();
 
-  // Returns the insets that should be applied around a DialogClientView. Note
-  // that the top inset is used for the distance between the buttons and the
-  // DialogClientView's content view.
-  virtual gfx::Insets GetDialogButtonInsets() const;
+  // Returns the insets metric according to the given enumeration element.
+  virtual gfx::Insets GetInsetsMetric(InsetsMetric metric) const;
 
-  // Returns the distance between a dialog's edge and the close button in the
-  // upper trailing corner.
-  virtual int GetDialogCloseButtonMargin() const;
-
-  // Returns the spacing between a pair of related horizontal buttons, used for
-  // dialog layout.
-  virtual int GetDialogRelatedButtonHorizontalSpacing() const;
-
-  // Returns the spacing between a pair of related vertical controls, used for
-  // dialog layout.
-  virtual int GetDialogRelatedControlVerticalSpacing() const;
-
-  // Returns the insets that should be applied around a dialog's frame view.
-  virtual gfx::Insets GetDialogFrameViewInsets() const;
-
-  // Returns the margins that should be applied around a bubble dialog.
-  virtual gfx::Insets GetBubbleDialogMargins() const;
-
-  // Returns the default minimum width of a button.
-  virtual int GetButtonMinimumWidth() const;
-
-  // Returns the minimum width of a dialog button.
-  virtual int GetDialogButtonMinimumWidth() const;
-
-  // Returns the default padding to add on each side of a button's label.
-  virtual int GetButtonHorizontalPadding() const;
+  // Returns the distance metric between elements according to the given
+  // enumeration element.
+  virtual int GetDistanceMetric(DistanceMetric metric) const;
 
  protected:
   ViewsDelegate();
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 448baf89..c64b0ca 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -187,8 +187,8 @@
       xwindow_(0),
       x_root_window_(DefaultRootWindow(xdisplay_)),
       atom_cache_(xdisplay_, kAtomsToCache),
-      window_mapped_(false),
-      wait_for_unmap_(false),
+      window_mapped_in_server_(false),
+      window_mapped_in_client_(false),
       is_fullscreen_(false),
       is_always_on_top_(false),
       use_native_frame_(false),
@@ -593,7 +593,7 @@
 }
 
 bool DesktopWindowTreeHostX11::IsVisible() const {
-  return window_mapped_ && !wait_for_unmap_;
+  return window_mapped_in_client_;
 }
 
 void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) {
@@ -831,9 +831,9 @@
   bool is_active =
       (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_;
 
-  // is_active => window_mapped_
-  // !window_mapped_ => !is_active
-  DCHECK(!is_active || window_mapped_);
+  // is_active => window_mapped_in_server_
+  // !window_mapped_in_server_ => !is_active
+  DCHECK(!is_active || window_mapped_in_server_);
 
   // |has_window_focus_| and |has_pointer_focus_| are mutually exclusive.
   DCHECK(!has_window_focus_ || !has_pointer_focus_);
@@ -1224,7 +1224,7 @@
 void DesktopWindowTreeHostX11::HideImpl() {
   if (IsVisible()) {
     XWithdrawWindow(xdisplay_, xwindow_, 0);
-    wait_for_unmap_ = true;
+    window_mapped_in_client_ = false;
   }
   native_widget_delegate_->OnNativeWidgetVisibilityChanged(false);
 }
@@ -1242,9 +1242,12 @@
   XWindowChanges changes = {0};
   unsigned value_mask = 0;
 
-  delayed_resize_task_.Cancel();
 
   if (size_changed) {
+    // Only cancel the delayed resize task if we're already about to call
+    // OnHostResized in this function.
+    delayed_resize_task_.Cancel();
+
     // Update the minimum and maximum sizes in case they have changed.
     UpdateMinAndMaxSize();
 
@@ -1664,9 +1667,6 @@
 }
 
 void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() {
-  if (!IsVisible())
-    return;
-
   gfx::Size minimum_in_pixels =
       ToPixelRect(gfx::Rect(native_widget_delegate_->GetMinimumSize())).size();
   gfx::Size maximum_in_pixels =
@@ -1937,19 +1937,10 @@
   ui::X11EventSource* event_source = ui::X11EventSource::GetInstance();
   DCHECK(event_source);
 
-  if (wait_for_unmap_) {
-    // Block until our window is unmapped. This avoids a race condition when
-    // remapping an unmapped window.
-    event_source->BlockUntilWindowUnmapped(xwindow_);
-    DCHECK(!wait_for_unmap_);
-  }
+  UpdateMinAndMaxSize();
 
   XMapWindow(xdisplay_, xwindow_);
-
-  // We now block until our window is mapped. Some X11 APIs will crash and
-  // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
-  // asynchronous.
-  event_source->BlockUntilWindowMapped(xwindow_);
+  window_mapped_in_client_ = true;
 }
 
 void DesktopWindowTreeHostX11::SetWindowTransparency() {
@@ -2172,13 +2163,11 @@
       break;
     }
     case MapNotify: {
-      window_mapped_ = true;
+      window_mapped_in_server_ = true;
 
       for (DesktopWindowTreeHostObserverX11& observer : observer_list_)
         observer.OnWindowMapped(xwindow_);
 
-      UpdateMinAndMaxSize();
-
       // Some WMs only respect maximize hints after the window has been mapped.
       // Check whether we need to re-do a maximization.
       if (should_maximize_after_map_) {
@@ -2189,8 +2178,7 @@
       break;
     }
     case UnmapNotify: {
-      window_mapped_ = false;
-      wait_for_unmap_ = false;
+      window_mapped_in_server_ = false;
       has_pointer_ = false;
       has_pointer_grab_ = false;
       has_pointer_focus_ = false;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index 544f086..757b780 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -291,13 +291,11 @@
 
   ui::X11AtomCache atom_cache_;
 
-  // Is the window mapped to the screen?
-  bool window_mapped_;
+  // Whether the window is mapped with respect to the X server.
+  bool window_mapped_in_server_;
 
-  // Should we wait for an UnmapNotify before trying to remap the window?
-  // If |wait_for_unmap_| is true, we have sent an XUnmapWindow request to the
-  // server and have yet to receive an UnmapNotify.
-  bool wait_for_unmap_;
+  // Whether the window is visible with respect to Aura.
+  bool window_mapped_in_client_;
 
   // The bounds of |xwindow_|.
   gfx::Rect bounds_in_pixels_;
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index fcf37d9f..5d543a7 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -75,8 +75,8 @@
 
 DialogClientView::DialogClientView(Widget* owner, View* contents_view)
     : ClientView(owner, contents_view),
-      button_row_insets_(
-          ViewsDelegate::GetInstance()->GetDialogButtonInsets()) {
+      button_row_insets_(ViewsDelegate::GetInstance()->GetInsetsMetric(
+          InsetsMetric::DIALOG_BUTTON)) {
   // Doing this now ensures this accelerator will have lower priority than
   // one set by the contents view.
   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
@@ -281,8 +281,8 @@
       button = MdTextButton::CreateSecondaryUiButton(this, title);
     }
 
-    const int minimum_width =
-        ViewsDelegate::GetInstance()->GetDialogButtonMinimumWidth();
+    const int minimum_width = ViewsDelegate::GetInstance()->GetDistanceMetric(
+        views::DistanceMetric::DIALOG_BUTTON_MINIMUM_WIDTH);
     button->SetMinSize(gfx::Size(minimum_width, 0));
 
     button->SetGroup(kButtonGroup);
@@ -301,8 +301,8 @@
   if (GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding))
     return extra_view_padding;
 
-  return ViewsDelegate::GetInstance()
-      ->GetDialogRelatedButtonHorizontalSpacing();
+  return ViewsDelegate::GetInstance()->GetDistanceMetric(
+      views::DistanceMetric::RELATED_BUTTON_HORIZONTAL);
 }
 
 std::array<View*, DialogClientView::kNumButtons>
@@ -341,8 +341,8 @@
   // They expect GetDialogRelatedControlVerticalSpacing() in this case.
   // TODO(tapted): Remove this under Harmony.
   if (insets.top() == 0) {
-    const int top =
-        ViewsDelegate::GetInstance()->GetDialogRelatedControlVerticalSpacing();
+    const int top = ViewsDelegate::GetInstance()->GetDistanceMetric(
+        views::DistanceMetric::RELATED_CONTROL_VERTICAL);
     insets.Set(top, insets.left(), insets.bottom(), insets.right());
   }
 
@@ -356,8 +356,8 @@
   // GetExtraViewSpacing() handles <pad+stretchy>.
   const int button_spacing =
       (ok_button_ && cancel_button_)
-          ? ViewsDelegate::GetInstance()
-                ->GetDialogRelatedButtonHorizontalSpacing()
+          ? ViewsDelegate::GetInstance()->GetDistanceMetric(
+                views::DistanceMetric::RELATED_BUTTON_HORIZONTAL)
           : 0;
 
   constexpr int kButtonRowId = 0;
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 333adf9..9122b10 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -200,9 +200,10 @@
 NonClientFrameView* DialogDelegate::CreateDialogFrameView(
     Widget* widget,
     const gfx::Insets& content_margins) {
-  BubbleFrameView* frame = new BubbleFrameView(
-      ViewsDelegate::GetInstance()->GetDialogFrameViewInsets(),
-      content_margins);
+  BubbleFrameView* frame =
+      new BubbleFrameView(ViewsDelegate::GetInstance()->GetInsetsMetric(
+                              InsetsMetric::DIALOG_FRAME_VIEW),
+                          content_margins);
   const BubbleBorder::Shadow kShadow = BubbleBorder::SMALL_SHADOW;
   std::unique_ptr<BubbleBorder> border(
       new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor));