diff --git a/DEPS b/DEPS
index c584445..9018480b 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # 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': 'b5464754e7d1884519d170fa994c64693a3f9112',
+  'v8_revision': '2783d5390f40a85a6588da2e2f2784fd9349ad86',
   # 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': 'ada8f7cc7d7aa2d0a7d18016c6510c1d5892bd17',
+  'catapult_revision': '643994eea8e5d5421441168190e8499c9cbf7ae8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
index d9aefd6b..fbadb25 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.infobar.translate.TranslateMenuHelper;
 import org.chromium.chrome.browser.infobar.translate.TranslateTabLayout;
 import org.chromium.chrome.browser.snackbar.Snackbar;
+import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.browser.widget.TintedImageButton;
 import org.chromium.ui.widget.Toast;
 
@@ -42,6 +43,26 @@
 
     private TintedImageButton mMenuButton;
 
+    /** The controller for translate UI snackbars. */
+    class TranslateSnackbarController implements SnackbarController {
+        private final int mMenuItemId;
+
+        public TranslateSnackbarController(int menuItemId) {
+            mMenuItemId = menuItemId;
+        }
+
+        @Override
+        public void onDismissNoAction(Object actionData) {
+            handleTranslateOptionPostSnackbar(mMenuItemId);
+        }
+
+        @Override
+        public void onAction(Object actionData) {
+            // TODO(ramyasharma): Add logging metric to track cancel actions.
+            // Do nothing.
+        }
+    };
+
     @CalledByNative
     private static InfoBar create(int initialStep, String sourceLanguageCode,
             String targetLanguageCode, boolean alwaysTranslate, boolean triggeredFromMenu,
@@ -178,24 +199,26 @@
                 mLanguageMenuHelper.show(TranslateMenu.MENU_TARGET_LANGUAGE);
                 return;
             case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE:
-                mOptions.toggleAlwaysTranslateLanguageState(
-                        !mOptions.alwaysTranslateLanguageState());
-                nativeApplyBoolTranslateOption(mNativeTranslateInfoBarPtr,
-                        TranslateOption.ALWAYS_TRANSLATE, mOptions.alwaysTranslateLanguageState());
                 // Only show snackbar when "Always Translate" is enabled.
-                if (mOptions.alwaysTranslateLanguageState()) {
-                    showSnackbar(TranslateSnackbarType.ALWAYS_TRANSLATE);
+                if (!mOptions.alwaysTranslateLanguageState()) {
+                    createAndShowSnackbar(
+                            getContext().getString(R.string.translate_snackbar_always_translate,
+                                    mOptions.sourceLanguageName(), mOptions.targetLanguageName()),
+                            Snackbar.UMA_TRANSLATE_ALWAYS, itemId);
+                } else {
+                    handleTranslateOptionPostSnackbar(itemId);
                 }
                 return;
             case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE:
-                nativeApplyBoolTranslateOption(
-                        mNativeTranslateInfoBarPtr, TranslateOption.NEVER_TRANSLATE, true);
-                showSnackbar(TranslateSnackbarType.NEVER_TRANSLATE);
+                createAndShowSnackbar(
+                        getContext().getString(R.string.translate_snackbar_language_never,
+                                mOptions.sourceLanguageName()),
+                        Snackbar.UMA_TRANSLATE_NEVER, itemId);
                 return;
             case TranslateMenu.ID_OVERFLOW_NEVER_SITE:
-                nativeApplyBoolTranslateOption(
-                        mNativeTranslateInfoBarPtr, TranslateOption.NEVER_TRANSLATE_SITE, true);
-                showSnackbar(TranslateSnackbarType.NEVER_TRANSLATE_SITE);
+                createAndShowSnackbar(
+                        getContext().getString(R.string.translate_snackbar_site_never),
+                        Snackbar.UMA_TRANSLATE_NEVER_SITE, itemId);
                 return;
             case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE:
                 initMenuHelper(TranslateMenu.MENU_SOURCE_LANGUAGE);
@@ -230,29 +253,37 @@
         }
     }
 
-    private void showSnackbar(int snackbarType) {
-        if (snackbarType == TranslateSnackbarType.NEVER_TRANSLATE) {
-            createAndShowSnackbar(getContext().getString(R.string.translate_snackbar_language_never,
-                                          mOptions.sourceLanguageName()),
-                    Snackbar.UMA_TRANSLATE_NEVER);
-        } else if (snackbarType == TranslateSnackbarType.ALWAYS_TRANSLATE) {
-            createAndShowSnackbar(
-                    getContext().getString(R.string.translate_snackbar_always_translate,
-                            mOptions.sourceLanguageName(), mOptions.targetLanguageName()),
-                    Snackbar.UMA_TRANSLATE_ALWAYS);
-        } else if (snackbarType == TranslateSnackbarType.NEVER_TRANSLATE_SITE) {
-            createAndShowSnackbar(getContext().getString(R.string.translate_snackbar_site_never),
-                    Snackbar.UMA_TRANSLATE_NEVER_SITE);
-        }
-    }
-
-    private void createAndShowSnackbar(String title, int type) {
+    private void createAndShowSnackbar(String title, int umaType, int itemId) {
         if (getSnackbarManager() == null) {
+            // Directly apply menu option, if snackbar system is not working.
+            handleTranslateOptionPostSnackbar(itemId);
             return;
         }
-        getSnackbarManager().showSnackbar(Snackbar.make(title, new TranslateSnackbarController(),
-                                                          Snackbar.TYPE_NOTIFICATION, type)
-                                                  .setSingleLine(false));
+        getSnackbarManager().showSnackbar(
+                Snackbar.make(title, new TranslateSnackbarController(itemId),
+                                Snackbar.TYPE_NOTIFICATION, umaType)
+                        .setSingleLine(false)
+                        .setAction(
+                                getContext().getString(R.string.translate_snackbar_cancel), null));
+    }
+
+    private void handleTranslateOptionPostSnackbar(int itemId) {
+        switch (itemId) {
+            case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE:
+                mOptions.toggleAlwaysTranslateLanguageState(
+                        !mOptions.alwaysTranslateLanguageState());
+                nativeApplyBoolTranslateOption(mNativeTranslateInfoBarPtr,
+                        TranslateOption.ALWAYS_TRANSLATE, mOptions.alwaysTranslateLanguageState());
+                return;
+            case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE:
+                nativeApplyBoolTranslateOption(
+                        mNativeTranslateInfoBarPtr, TranslateOption.NEVER_TRANSLATE, true);
+                return;
+            case TranslateMenu.ID_OVERFLOW_NEVER_SITE:
+                nativeApplyBoolTranslateOption(
+                        mNativeTranslateInfoBarPtr, TranslateOption.NEVER_TRANSLATE_SITE, true);
+                return;
+        }
     }
 
     private native void nativeApplyStringTranslateOption(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateSnackbarController.java
deleted file mode 100644
index 21db1ae..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateSnackbarController.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.infobar;
-
-import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
-
-/**
- * The controller for translate UI snackbars.
- */
-class TranslateSnackbarController implements SnackbarController {
-    @Override
-    public void onDismissNoAction(Object actionData) {
-        // No action.
-    }
-
-    @Override
-    public void onAction(Object actionData) {
-        nativeToggleTranslateOption(((Long) actionData).longValue());
-    }
-
-    private native void nativeToggleTranslateOption(long nativeTranslateSnackbar);
-};
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index e9b2afd1..261d895 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -474,7 +474,6 @@
   "java/src/org/chromium/chrome/browser/infobar/TranslateNeverPanel.java",
   "java/src/org/chromium/chrome/browser/infobar/TranslateOptions.java",
   "java/src/org/chromium/chrome/browser/infobar/TranslateSubPanel.java",
-  "java/src/org/chromium/chrome/browser/infobar/TranslateSnackbarController.java",
   "java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/translate/TranslateMenu.java",
   "java/src/org/chromium/chrome/browser/infobar/translate/TranslateMenuHelper.java",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 28a0089..9092afd 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4150,7 +4150,6 @@
       "../android/java/src/org/chromium/chrome/browser/infobar/SubresourceFilterInfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/TranslateInfoBar.java",
-      "../android/java/src/org/chromium/chrome/browser/infobar/TranslateSnackbarController.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsSettings.java",
       "../android/java/src/org/chromium/chrome/browser/invalidation/InvalidationServiceFactory.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6b4eb07..16a1c17 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1210,6 +1210,9 @@
     {"top-chrome-md", flag_descriptions::kTopChromeMd,
      flag_descriptions::kTopChromeMdDescription, kOsDesktop,
      MULTI_VALUE_TYPE(kTopChromeMaterialDesignChoices)},
+    {"enable-site-details", flag_descriptions::kSiteDetails,
+     flag_descriptions::kSiteDetailsDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kSiteDetails)},
     {"enable-site-settings", flag_descriptions::kSiteSettings,
      flag_descriptions::kSiteSettingsDescription, kOsDesktop,
      SINGLE_VALUE_TYPE(switches::kEnableSiteSettings)},
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index 09a17372..70194d7 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -246,7 +246,11 @@
   DCHECK(!callback.is_null());
 
   // If ARC is not available, skip the check.
-  if (!IsArcAvailable()) {
+  // This shortcut is just for merginally improving the log-in performance on
+  // old devices without ARC. We can always safely remove the following 4 lines
+  // without changing any functionality when, say, the code clarity becomes
+  // more important in the future.
+  if (!IsArcAvailable() && !IsArcKioskAvailable()) {
     callback.Run();
     return;
   }
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 71e11e2d..71c0017a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -243,6 +243,12 @@
 
 const char kTopChromeMdMaterialHybrid[] = "Touch";
 
+const char kSiteDetails[] = "Site Details";
+
+const char kSiteDetailsDescription[] =
+    "Adds UI in MD Settings to view all content settings for a specific "
+    "origin.";
+
 const char kSiteSettings[] = "Site settings with All sites and Site details";
 
 const char kSiteSettingsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 42d0adf..437edc3 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -252,6 +252,13 @@
 // Top Chrome material hybrid design option (for touchscreens).
 extern const char kTopChromeMdMaterialHybrid[];
 
+// Title of the flag which enables site details in MD settings.
+extern const char kSiteDetails[];
+
+// Description of the flag which enables or disables site details in MD
+// settings.
+extern const char kSiteDetailsDescription[];
+
 // Title of the flag which enables the site settings all sites list and site
 // details.
 extern const char kSiteSettings[];
diff --git a/chrome/browser/resources/md_bookmarks/app.html b/chrome/browser/resources/md_bookmarks/app.html
index 4d997e4..e35f026 100644
--- a/chrome/browser/resources/md_bookmarks/app.html
+++ b/chrome/browser/resources/md_bookmarks/app.html
@@ -31,8 +31,17 @@
       }
 
       #splitter {
+        box-sizing: border-box;
         cursor: e-resize;
         flex: 0 0 var(--splitter-width);
+        opacity: 0;
+      }
+
+      #splitter:hover,
+      #splitter.splitter-active {
+        -webkit-border-start: 1px solid rgba(0, 0, 0, 0.1);
+        opacity: 1;
+        transition: opacity 100ms ease-out;
       }
 
       :host-context([dir='rtl']) #splitter {
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 9a465f5..5b84fda 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -59,7 +59,7 @@
 namespace {
 
 const base::Feature kNtpTilesFeature{"NTPTilesInInstantService",
-                                     base::FEATURE_ENABLED_BY_DEFAULT};
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace
 
diff --git a/chrome/browser/ui/android/snackbars/translate_snackbar.cc b/chrome/browser/ui/android/snackbars/translate_snackbar.cc
deleted file mode 100644
index fd7c8b1..0000000
--- a/chrome/browser/ui/android/snackbars/translate_snackbar.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/android/snackbars/translate_snackbar.h"
-
-#include "base/memory/ptr_util.h"
-#include "jni/TranslateSnackbarController_jni.h"
-
-TranslateSnackbar::TranslateSnackbar(int snackbar_type) {
-  type_ = snackbar_type;
-}
-
-void TranslateSnackbar::ToggleTranslateOption(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) {
-  // TODO(ramyasharma): Implement.
-}
-
-// Native JNI methods -------------------------------------------------------
-
-// static
-bool RegisterTranslateSnackbar(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
diff --git a/chrome/browser/ui/android/snackbars/translate_snackbar.h b/chrome/browser/ui/android/snackbars/translate_snackbar.h
deleted file mode 100644
index dce84997..0000000
--- a/chrome/browser/ui/android/snackbars/translate_snackbar.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_ANDROID_SNACKBARS_TRANSLATE_SNACKBAR_H_
-#define CHROME_BROWSER_UI_ANDROID_SNACKBARS_TRANSLATE_SNACKBAR_H_
-
-#include <stddef.h>
-
-#include "base/android/jni_android.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/macros.h"
-
-class TranslateSnackbar {
- public:
-  explicit TranslateSnackbar(int snackbar_type);
-
-  void ToggleTranslateOption(JNIEnv* env,
-                             const base::android::JavaParamRef<jobject>& obj);
-
- private:
-  int type_;
-
-  DISALLOW_COPY_AND_ASSIGN(TranslateSnackbar);
-};
-
-// Registers the native methods through JNI.
-bool RegisterTranslateSnackbar(JNIEnv* env);
-
-#endif  // CHROME_BROWSER_UI_ANDROID_SNACKBARS_TRANSLATE_SNACKBAR_H_
diff --git a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.h b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.h
index f05035b..76520037 100644
--- a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.h
+++ b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.h
@@ -80,6 +80,7 @@
   gfx::Rect GetClientAreaBoundsInScreen() const override;
   bool IsImmersiveModeEnabled() override;
   gfx::Rect GetTopContainerBoundsInScreen() override;
+  void DestroyAnyExclusiveAccessBubble() override;
 
  private:
   BrowserWindow* GetBrowserWindow() const;
diff --git a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
index cbe83eba..f60f66d5 100644
--- a/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
+++ b/chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.mm
@@ -193,6 +193,11 @@
   return gfx::Rect();
 }
 
+void ExclusiveAccessController::DestroyAnyExclusiveAccessBubble() {
+  Destroy();
+  new_back_shortcut_bubble_.reset();
+}
+
 BrowserWindow* ExclusiveAccessController::GetBrowserWindow() const {
   return [controller_ browserWindow];
 }
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
index 55e44ee9..784d16d 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.cc
@@ -88,3 +88,10 @@
 void FullscreenControllerTest::SetPrivilegedFullscreen(bool is_privileged) {
   GetFullscreenController()->SetPrivilegedFullscreenForTesting(is_privileged);
 }
+
+void FullscreenControllerTest::EnterActiveTabFullscreen() {
+  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
+  FullscreenNotificationObserver fullscreen_observer;
+  browser()->EnterFullscreenModeForTab(tab, GURL());
+  fullscreen_observer.Wait();
+}
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
index ccf86e7b..daab667 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
@@ -51,6 +51,8 @@
   void GoBack();
   void Reload();
   void SetPrivilegedFullscreen(bool is_privileged);
+  void EnterActiveTabFullscreen();
+
   static const char kFullscreenMouseLockHTML[];
   FullscreenController* GetFullscreenController();
   ExclusiveAccessManager* GetExclusiveAccessManager();
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index 0148bf2..6ba1064 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -75,7 +75,7 @@
   const int button_padding =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
   const int button_size_limit =
-      provider->GetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH);
+      provider->GetDistanceMetric(views::DISTANCE_BUTTON_MAX_LINKABLE_WIDTH);
 
   views::ColumnSet* column_set = layout->AddColumnSet(column_layout_id);
   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 0,
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views.cc b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
index b556c88..7040d6b 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views.cc
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
@@ -282,6 +282,23 @@
   UpdateMouseWatcher();
 }
 
+void ExclusiveAccessBubbleViews::OnWidgetDestroyed(views::Widget* widget) {
+  // Although SubtleNotificationView uses WIDGET_OWNS_NATIVE_WIDGET, a close can
+  // originate from the OS or some Chrome shutdown codepaths that bypass the
+  // destructor.
+  views::Widget* popup_on_stack = popup_;
+  DCHECK(popup_on_stack->HasObserver(this));
+
+  // Get ourselves destroyed. Calling ExitExclusiveAccess() won't work because
+  // the parent window might be destroyed as well, so asking it to exit
+  // fullscreen would be a bad idea.
+  bubble_view_context_->DestroyAnyExclusiveAccessBubble();
+
+  // Note: |this| is destroyed on the line above. Check that the destructor was
+  // invoked. This is safe to do since |popup_| is deleted via a posted task.
+  DCHECK(!popup_on_stack->HasObserver(this));
+}
+
 void ExclusiveAccessBubbleViews::OnWidgetVisibilityChanged(
     views::Widget* widget,
     bool visible) {
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views.h b/chrome/browser/ui/views/exclusive_access_bubble_views.h
index 46d06a7..fb93a95 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views.h
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views.h
@@ -61,7 +61,7 @@
   // Returns the root view containing |browser_view_|.
   views::View* GetBrowserRootView() const;
 
-  // ExclusiveAccessBubble overrides:
+  // ExclusiveAccessBubble:
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationEnded(const gfx::Animation* animation) override;
   gfx::Rect GetPopupRect(bool ignore_animation_state) const override;
@@ -73,12 +73,13 @@
   bool IsAnimating() override;
   bool CanMouseTriggerSlideIn() const override;
 
-  // content::NotificationObserver override:
+  // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // views::WidgetObserver override:
+  // views::WidgetObserver:
+  void OnWidgetDestroyed(views::Widget* widget) override;
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
 
   // views::LinkListener override:
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views_context.h b/chrome/browser/ui/views/exclusive_access_bubble_views_context.h
index d2300ea..e5da3b36 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views_context.h
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views_context.h
@@ -47,6 +47,11 @@
 
   // Returns the bounds of the top level View in screen coordinate system.
   virtual gfx::Rect GetTopContainerBoundsInScreen() = 0;
+
+  // Destroy any exclusive access bubble. This allows the bubble to ask its
+  // owner to clean up when the bubble observes its native widget being
+  // destroyed before the owner requested it.
+  virtual void DestroyAnyExclusiveAccessBubble() = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_EXCLUSIVE_ACCESS_BUBBLE_VIEWS_CONTEXT_H_
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc
new file mode 100644
index 0000000..71f3942d
--- /dev/null
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
+#include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+class ExclusiveAccessBubbleViewsTest : public FullscreenControllerTest,
+                                       public views::WidgetObserver {
+ public:
+  ExclusiveAccessBubbleViewsTest() {}
+
+  ExclusiveAccessBubbleViews* bubble() {
+    BrowserView* browser_view =
+        BrowserView::GetBrowserViewForBrowser(browser());
+    return browser_view->exclusive_access_bubble();
+  }
+
+  // WidgetObserver:
+  void OnWidgetDestroying(views::Widget* widget) override {
+    was_observing_in_destroying_ = widget->HasObserver(bubble());
+    was_destroying_ = true;
+    widget->RemoveObserver(this);
+  }
+
+ protected:
+  bool was_destroying_ = false;
+  bool was_observing_in_destroying_ = false;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExclusiveAccessBubbleViewsTest);
+};
+
+// Simulate obscure codepaths resulting in the bubble Widget being closed before
+// the ExclusiveAccessBubbleViews destructor asks for it. If a close bypasses
+// the destructor, animations could still be running that attempt to manipulate
+// a destroyed Widget and crash.
+IN_PROC_BROWSER_TEST_F(ExclusiveAccessBubbleViewsTest, NativeClose) {
+  EXPECT_FALSE(bubble());
+  EnterActiveTabFullscreen();
+  EXPECT_TRUE(bubble());
+
+  bubble()->GetView()->GetWidget()->AddObserver(this);
+
+  // Simulate the bubble being closed out from under its controller, which seems
+  // to happen in some odd corner cases, like system log-off while the bubble is
+  // showing.
+  bubble()->GetView()->GetWidget()->CloseNow();
+  EXPECT_FALSE(bubble());
+
+  // Verify that teardown is really happening via OnWidgetDestroyed() rather
+  // than the usual path via the ExclusiveAccessBubbleViews destructor. Since
+  // the destructor always first removes ExclusiveAccessBubbleViews as an
+  // observer before starting the close, checking in OnWidgetDestroyed that it's
+  // still observing achieves this.
+  EXPECT_TRUE(was_observing_in_destroying_);
+  EXPECT_TRUE(was_destroying_);
+}
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 66f94a6..cd473a1 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2694,6 +2694,11 @@
   return top_container_->GetBoundsInScreen();
 }
 
+void BrowserView::DestroyAnyExclusiveAccessBubble() {
+  exclusive_access_bubble_.reset();
+  new_back_shortcut_bubble_.reset();
+}
+
 extensions::ActiveTabPermissionGranter*
 BrowserView::GetActiveTabPermissionGranter() {
   content::WebContents* web_contents = GetActiveWebContents();
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index c1a2b41..c79b85cb 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -465,6 +465,7 @@
   gfx::Rect GetClientAreaBoundsInScreen() const override;
   bool IsImmersiveModeEnabled() override;
   gfx::Rect GetTopContainerBoundsInScreen() override;
+  void DestroyAnyExclusiveAccessBubble() override;
 
   // extension::ExtensionKeybindingRegistry::Delegate overrides
   extensions::ActiveTabPermissionGranter* GetActiveTabPermissionGranter()
diff --git a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
index dd6b727b..6bb3edf 100644
--- a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
@@ -26,8 +26,6 @@
 
 int ChromeLayoutProvider::GetDistanceMetric(int metric) const {
   switch (metric) {
-    case DISTANCE_BUTTON_MAX_LINKABLE_WIDTH:
-      return 0;
     case DISTANCE_BUTTON_MINIMUM_WIDTH:
       return views::kMinimumButtonWidth;
     case DISTANCE_CONTROL_LIST_VERTICAL:
diff --git a/chrome/browser/ui/views/harmony/chrome_layout_provider.h b/chrome/browser/ui/views/harmony/chrome_layout_provider.h
index b78c155..18be5ff 100644
--- a/chrome/browser/ui/views/harmony/chrome_layout_provider.h
+++ b/chrome/browser/ui/views/harmony/chrome_layout_provider.h
@@ -13,12 +13,8 @@
 #include "ui/views/layout/layout_provider.h"
 
 enum ChromeDistanceMetric {
-  // The maximum width a button can have and still influence the sizes of
-  // other linked buttons.  This allows short buttons to have linked widths
-  // without long buttons making things overly wide.
-  DISTANCE_BUTTON_MAX_LINKABLE_WIDTH = views::VIEWS_DISTANCE_END,
   // Default minimum width of a button.
-  DISTANCE_BUTTON_MINIMUM_WIDTH,
+  DISTANCE_BUTTON_MINIMUM_WIDTH = views::VIEWS_DISTANCE_END,
   // Vertical spacing between a list of multiple controls in one column.
   DISTANCE_CONTROL_LIST_VERTICAL,
   // Margin between the edge of a dialog and the left, right, or bottom of a
diff --git a/chrome/browser/ui/views/harmony/harmony_layout_provider.cc b/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
index d9dbd4df..d11ef463 100644
--- a/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/harmony_layout_provider.cc
@@ -57,8 +57,8 @@
              2 * GetDistanceMetric(views::DISTANCE_BUTTON_HORIZONTAL_PADDING);
     case views::DISTANCE_BUTTON_HORIZONTAL_PADDING:
       return kHarmonyLayoutUnit;
-    case DISTANCE_BUTTON_MAX_LINKABLE_WIDTH:
-      return kHarmonyLayoutUnit * 8;
+    case views::DISTANCE_BUTTON_MAX_LINKABLE_WIDTH:
+      return kHarmonyLayoutUnit * 7;
     case DISTANCE_RELATED_LABEL_HORIZONTAL:
       return kHarmonyLayoutUnit;
     case DISTANCE_SUBSECTION_HORIZONTAL_INDENT:
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index b5eb4956..73b462d 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -217,6 +217,7 @@
   // views::BubbleDialogDelegateView:
   void OnWidgetDestroying(views::Widget* widget) override;
   int GetDialogButtons() const override;
+  bool ShouldSnapFrameWidth() const override;
 
  private:
   friend class PageInfoBubbleView;
@@ -378,6 +379,10 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
+bool InternalPageInfoBubbleView::ShouldSnapFrameWidth() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // PageInfoBubbleView
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index ecd94eb1..7cd2d31c 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -300,6 +300,11 @@
 const base::Feature kSimplifiedFullscreenUI{"ViewsSimplifiedFullscreenUI",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables or disables UI in MD Settings to view content settings grouped by
+// origin.
+const base::Feature kSiteDetails{"SiteDetails",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
+
 #if defined(SYZYASAN)
 // Enable the deferred free mechanism in the syzyasan module, which helps the
 // performance by deferring some work on the critical path to a background
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index d6ecedc8..4aae3c88 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -164,6 +164,8 @@
 
 extern const base::Feature kSimplifiedFullscreenUI;
 
+extern const base::Feature kSiteDetails;
+
 #if defined(SYZYASAN)
 extern const base::Feature kSyzyasanDeferredFree;
 #endif
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b2916324..129e8c84 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -542,6 +542,7 @@
           "../browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h",
           "../browser/ui/views/certificate_selector_browsertest.cc",
           "../browser/ui/views/constrained_window_views_browsertest.cc",
+          "../browser/ui/views/exclusive_access_bubble_views_interactive_uitest.cc",
           "../browser/ui/views/extensions/extension_dialog_interactive_uitest.cc",
           "../browser/ui/views/find_bar_views_interactive_uitest.cc",
           "../browser/ui/views/frame/browser_view_focus_uitest.cc",
diff --git a/components/content_settings/core/browser/BUILD.gn b/components/content_settings/core/browser/BUILD.gn
index 3e4fa66..44e44e3 100644
--- a/components/content_settings/core/browser/BUILD.gn
+++ b/components/content_settings/core/browser/BUILD.gn
@@ -54,6 +54,7 @@
     "//components/url_formatter",
     "//extensions/features",
     "//net",
+    "//services/preferences/public/cpp",
     "//url",
   ]
 
diff --git a/components/content_settings/core/browser/DEPS b/components/content_settings/core/browser/DEPS
index eddae7b..01dbe2a 100644
--- a/components/content_settings/core/browser/DEPS
+++ b/components/content_settings/core/browser/DEPS
@@ -8,4 +8,5 @@
   "+extensions/features",
   "+net/base",
   "+net/cookies",
+  "+services/preferences/public",
 ]
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index d1bf46e..d26b155 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -8,6 +8,7 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -20,6 +21,8 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "services/preferences/public/cpp/dictionary_value_update.h"
+#include "services/preferences/public/cpp/scoped_pref_update.h"
 #include "url/gurl.h"
 
 namespace {
@@ -179,9 +182,8 @@
 
   {
     base::AutoReset<bool> auto_reset(&updating_preferences_, true);
-    DictionaryPrefUpdate update(prefs_, pref_name_);
-    base::DictionaryValue* pattern_pairs_settings = update.Get();
-    pattern_pairs_settings->Clear();
+    prefs::ScopedDictionaryPrefUpdate update(prefs_, pref_name_);
+    update->Clear();
   }
 }
 
@@ -214,13 +216,13 @@
 }
 
 void ContentSettingsPref::ReadContentSettingsFromPref() {
-  // |DictionaryPrefUpdate| sends out notifications when destructed. This
+  // |ScopedDictionaryPrefUpdate| sends out notifications when destructed. This
   // construction order ensures |AutoLock| gets destroyed first and |lock_| is
   // not held when the notifications are sent. Also, |auto_reset| must be still
   // valid when the notifications are sent, so that |Observe| skips the
   // notification.
   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
-  DictionaryPrefUpdate update(prefs_, pref_name_);
+  prefs::ScopedDictionaryPrefUpdate update(prefs_, pref_name_);
   base::AutoLock auto_lock(lock_);
 
   const base::DictionaryValue* all_settings_dictionary =
@@ -233,23 +235,26 @@
   if (!all_settings_dictionary)
     return;
 
-  base::DictionaryValue* mutable_settings;
-  std::unique_ptr<base::DictionaryValue> mutable_settings_scope;
+  const base::DictionaryValue* settings;
 
   if (!is_incognito_) {
-    mutable_settings = update.Get();
+    // Convert all Unicode patterns into punycode form, then read.
+    auto mutable_settings = update.Get();
+    CanonicalizeContentSettingsExceptions(mutable_settings.get());
+    settings = mutable_settings->AsConstDictionary();
   } else {
-    // Create copy as we do not want to persist anything in incognito prefs.
-    mutable_settings = all_settings_dictionary->DeepCopy();
-    mutable_settings_scope.reset(mutable_settings);
+    // Canonicalization is unnecessary when |is_incognito_|. Both incognito and
+    // non-incognito read from the same pref and non-incognito reads occur
+    // before incognito reads. Thus, by the time the incognito call to
+    // ReadContentSettingsFromPref() occurs, the non-incognito call will have
+    // canonicalized the stored pref data.
+    settings = all_settings_dictionary;
   }
-  // Convert all Unicode patterns into punycode form, then read.
-  CanonicalizeContentSettingsExceptions(mutable_settings);
 
   size_t cookies_block_exception_count = 0;
   size_t cookies_allow_exception_count = 0;
   size_t cookies_session_only_exception_count = 0;
-  for (base::DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
+  for (base::DictionaryValue::Iterator i(*settings); !i.IsAtEnd();
        i.Advance()) {
     const std::string& pattern_str(i.key());
     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
@@ -349,39 +354,41 @@
     const base::Time last_modified,
     const base::Value* value) {
   // Ensure that |lock_| is not held by this thread, since this function will
-  // send out notifications (by |~DictionaryPrefUpdate|).
+  // send out notifications (by |~ScopedDictionaryPrefUpdate|).
   AssertLockNotHeld();
 
   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
   {
-    DictionaryPrefUpdate update(prefs_, pref_name_);
-    base::DictionaryValue* pattern_pairs_settings = update.Get();
+    prefs::ScopedDictionaryPrefUpdate update(prefs_, pref_name_);
+    std::unique_ptr<prefs::DictionaryValueUpdate> pattern_pairs_settings =
+        update.Get();
 
     // Get settings dictionary for the given patterns.
     std::string pattern_str(CreatePatternString(primary_pattern,
                                                 secondary_pattern));
-    base::DictionaryValue* settings_dictionary = nullptr;
+    std::unique_ptr<prefs::DictionaryValueUpdate> settings_dictionary;
     bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
         pattern_str, &settings_dictionary);
 
     if (!found && value) {
-      settings_dictionary = new base::DictionaryValue;
-      pattern_pairs_settings->SetWithoutPathExpansion(
-          pattern_str, settings_dictionary);
+      settings_dictionary =
+          pattern_pairs_settings->SetDictionaryWithoutPathExpansion(
+              pattern_str, base::MakeUnique<base::DictionaryValue>());
     }
 
     if (settings_dictionary) {
       if (SupportsResourceIdentifiers(content_type_) &&
           !resource_identifier.empty()) {
-        base::DictionaryValue* resource_dictionary = nullptr;
+        std::unique_ptr<prefs::DictionaryValueUpdate> resource_dictionary;
         found = settings_dictionary->GetDictionary(
             kPerResourceIdentifierPrefName, &resource_dictionary);
         if (!found) {
           if (value == nullptr)
             return;  // Nothing to remove. Exit early.
-          resource_dictionary = new base::DictionaryValue;
-          settings_dictionary->Set(
-              kPerResourceIdentifierPrefName, resource_dictionary);
+          resource_dictionary =
+              settings_dictionary->SetDictionaryWithoutPathExpansion(
+                  kPerResourceIdentifierPrefName,
+                  base::MakeUnique<base::DictionaryValue>());
         }
         // Update resource dictionary.
         if (value == nullptr) {
@@ -394,8 +401,8 @@
                                                             nullptr);
           }
         } else {
-          resource_dictionary->SetWithoutPathExpansion(
-              resource_identifier, value->DeepCopy());
+          resource_dictionary->SetWithoutPathExpansion(resource_identifier,
+                                                       value->CreateDeepCopy());
           // Update timestamp for whole resource dictionary.
           settings_dictionary->SetStringWithoutPathExpansion(
               kLastModifiedPath,
@@ -409,8 +416,8 @@
           settings_dictionary->RemoveWithoutPathExpansion(kLastModifiedPath,
                                                           nullptr);
         } else {
-          settings_dictionary->SetWithoutPathExpansion(
-              kSettingPath, value->DeepCopy());
+          settings_dictionary->SetWithoutPathExpansion(kSettingPath,
+                                                       value->CreateDeepCopy());
           settings_dictionary->SetStringWithoutPathExpansion(
               kLastModifiedPath,
               base::Int64ToString(last_modified.ToInternalValue()));
@@ -427,14 +434,14 @@
 
 // static
 void ContentSettingsPref::CanonicalizeContentSettingsExceptions(
-    base::DictionaryValue* all_settings_dictionary) {
+    prefs::DictionaryValueUpdate* all_settings_dictionary) {
   DCHECK(all_settings_dictionary);
 
   std::vector<std::string> remove_items;
   base::StringPairs move_items;
-  for (base::DictionaryValue::Iterator i(*all_settings_dictionary);
-       !i.IsAtEnd();
-       i.Advance()) {
+  for (base::DictionaryValue::Iterator i(
+           *all_settings_dictionary->AsConstDictionary());
+       !i.IsAtEnd(); i.Advance()) {
     const std::string& pattern_str(i.key());
     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
          ParsePatternString(pattern_str);
@@ -478,7 +485,7 @@
     all_settings_dictionary->RemoveWithoutPathExpansion(
         move_items[i].first, &pattern_settings_dictionary);
     all_settings_dictionary->SetWithoutPathExpansion(
-        move_items[i].second, pattern_settings_dictionary.release());
+        move_items[i].second, std::move(pattern_settings_dictionary));
   }
 }
 
diff --git a/components/content_settings/core/browser/content_settings_pref.h b/components/content_settings/core/browser/content_settings_pref.h
index 356176c6..697425d4 100644
--- a/components/content_settings/core/browser/content_settings_pref.h
+++ b/components/content_settings/core/browser/content_settings_pref.h
@@ -23,8 +23,8 @@
 class PrefService;
 class PrefChangeRegistrar;
 
-namespace base {
-class DictionaryValue;
+namespace prefs {
+class DictionaryValueUpdate;
 }
 
 namespace content_settings {
@@ -92,7 +92,7 @@
                   const base::Value* value);
 
   static void CanonicalizeContentSettingsExceptions(
-      base::DictionaryValue* all_settings_dictionary);
+      prefs::DictionaryValueUpdate* all_settings_dictionary);
 
   // In the debug mode, asserts that |lock_| is not held by this thread. It's
   // ok if some other thread holds |lock_|, as long as it will eventually
diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc
index 98ab6245d..e7ac35f3c 100644
--- a/components/content_settings/core/browser/content_settings_pref_provider.cc
+++ b/components/content_settings/core/browser/content_settings_pref_provider.cc
@@ -28,6 +28,8 @@
 #include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "services/preferences/public/cpp/dictionary_value_update.h"
+#include "services/preferences/public/cpp/scoped_pref_update.h"
 
 namespace content_settings {
 
@@ -252,11 +254,11 @@
     if (!prefs_->GetDictionary(info->pref_name()))
       continue;
 
-    DictionaryPrefUpdate update(prefs_, info->pref_name());
-    base::DictionaryValue* all_settings = update.Get();
+    prefs::ScopedDictionaryPrefUpdate update(prefs_, info->pref_name());
+    auto all_settings = update.Get();
     std::vector<std::string> values_to_clean;
-    for (base::DictionaryValue::Iterator i(*all_settings); !i.IsAtEnd();
-         i.Advance()) {
+    for (base::DictionaryValue::Iterator i(*all_settings->AsConstDictionary());
+         !i.IsAtEnd(); i.Advance()) {
       const base::DictionaryValue* pattern_settings = nullptr;
       bool is_dictionary = i.value().GetAsDictionary(&pattern_settings);
       DCHECK(is_dictionary);
@@ -265,7 +267,7 @@
     }
 
     for (const std::string& key : values_to_clean) {
-      base::DictionaryValue* pattern_settings = nullptr;
+      std::unique_ptr<prefs::DictionaryValueUpdate> pattern_settings;
       all_settings->GetDictionaryWithoutPathExpansion(key, &pattern_settings);
       pattern_settings->RemoveWithoutPathExpansion(kObsoleteLastUsed, nullptr);
       if (pattern_settings->empty())
diff --git a/components/subresource_filter/core/common/url_pattern_index_unittest.cc b/components/subresource_filter/core/common/url_pattern_index_unittest.cc
index 1052d5ba..fcdb9da 100644
--- a/components/subresource_filter/core/common/url_pattern_index_unittest.cc
+++ b/components/subresource_filter/core/common/url_pattern_index_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/subresource_filter/core/common/url_pattern_index.h"
 
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <vector>
@@ -54,6 +55,14 @@
         IsThirdParty(url, document_origin), disable_generic_rules);
   }
 
+  bool IsOutOfRange(const flat::UrlRule* rule) const {
+    if (!rule)
+      return false;
+    const auto* data = reinterpret_cast<const uint8_t*>(rule);
+    return data < flat_builder_->GetBufferPointer() ||
+           data >= flat_builder_->GetBufferPointer() + flat_builder_->GetSize();
+  }
+
   void Reset() {
     index_matcher_.reset();
     index_builder_.reset();
@@ -665,4 +674,34 @@
       FindMatch("http://example.com", nullptr, kNoElement, kGenericBlock));
 }
 
+TEST_F(UrlPatternIndexTest, FindMatchReturnsCorrectRules) {
+  constexpr size_t kNumOfPatterns = 1024;
+
+  std::vector<std::string> url_patterns(kNumOfPatterns);
+  for (size_t i = 0; i < kNumOfPatterns; ++i) {
+    url_patterns[i] = "http://example." + std::to_string(i) + ".com";
+    ASSERT_TRUE(
+        AddUrlRule(MakeUrlRule(UrlPattern(url_patterns[i], kSubstring))))
+        << "Rule #" << i;
+  }
+  Finish();
+
+  std::reverse(url_patterns.begin() + kNumOfPatterns / 2, url_patterns.end());
+  for (const std::string& url_pattern : url_patterns) {
+    SCOPED_TRACE(::testing::Message() << "UrlPattern: " << url_pattern);
+
+    const flat::UrlRule* rule = FindMatch(url_pattern);
+    ASSERT_TRUE(rule);
+    ASSERT_FALSE(IsOutOfRange(rule));
+
+    const flatbuffers::String* rule_pattern = rule->url_pattern();
+    ASSERT_TRUE(rule_pattern);
+    EXPECT_EQ(url_pattern,
+              base::StringPiece(rule_pattern->data(), rule_pattern->size()));
+  }
+
+  EXPECT_FALSE(
+      FindMatch("http://example." + std::to_string(kNumOfPatterns) + ".com"));
+}
+
 }  // namespace subresource_filter
diff --git a/components/suggestions/suggestions_service_impl.cc b/components/suggestions/suggestions_service_impl.cc
index 6e41445d..5a013c8 100644
--- a/components/suggestions/suggestions_service_impl.cc
+++ b/components/suggestions/suggestions_service_impl.cc
@@ -46,37 +46,6 @@
 
 namespace {
 
-// Establishes the different sync states that matter to SuggestionsService.
-// There are three different concepts in the sync service: initialized, sync
-// enabled and history sync enabled.
-enum SyncState {
-  // State: Sync service is not initialized, yet not disabled. History sync
-  //     state is unknown (since not initialized).
-  // Behavior: Does not issue a server request, but serves from cache if
-  //     available.
-  NOT_INITIALIZED_ENABLED,
-
-  // State: Sync service is initialized, sync is enabled and history sync is
-  //     enabled.
-  // Behavior: Update suggestions from the server. Serve from cache on timeout.
-  INITIALIZED_ENABLED_HISTORY,
-
-  // State: Sync service is disabled or history sync is disabled.
-  // Behavior: Do not issue a server request. Clear the cache. Serve empty
-  //     suggestions.
-  SYNC_OR_HISTORY_SYNC_DISABLED,
-};
-
-SyncState GetSyncState(syncer::SyncService* sync) {
-  if (!sync || !sync->CanSyncStart() || sync->IsLocalSyncEnabled())
-    return SYNC_OR_HISTORY_SYNC_DISABLED;
-  if (!sync->IsSyncActive() || !sync->ConfigurationDone())
-    return NOT_INITIALIZED_ENABLED;
-  return sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES)
-             ? INITIALIZED_ENABLED_HISTORY
-             : SYNC_OR_HISTORY_SYNC_DISABLED;
-}
-
 // Used to UMA log the state of the last response from the server.
 enum SuggestionsResponseState {
   RESPONSE_EMPTY,
@@ -150,6 +119,7 @@
       token_service_(token_service),
       sync_service_(sync_service),
       sync_service_observer_(this),
+      sync_state_(INITIALIZED_ENABLED_HISTORY),
       url_request_context_(url_request_context),
       suggestions_store_(std::move(suggestions_store)),
       thumbnail_manager_(std::move(thumbnail_manager)),
@@ -157,8 +127,9 @@
       scheduling_delay_(TimeDelta::FromSeconds(kDefaultSchedulingDelaySec)),
       weak_ptr_factory_(this) {
   // |sync_service_| is null if switches::kDisableSync is set (tests use that).
-  if (sync_service_)
+  if (sync_service_) {
     sync_service_observer_.Add(sync_service_);
+  }
   // Immediately get the current sync state, so we'll flush the cache if
   // necessary.
   OnStateChanged(sync_service_);
@@ -169,8 +140,9 @@
 bool SuggestionsServiceImpl::FetchSuggestionsData() {
   DCHECK(thread_checker_.CalledOnValidThread());
   // If sync state allows, issue a network request to refresh the suggestions.
-  if (GetSyncState(sync_service_) != INITIALIZED_ENABLED_HISTORY)
+  if (sync_state_ != INITIALIZED_ENABLED_HISTORY) {
     return false;
+  }
   IssueRequestIfNoneOngoing(BuildSuggestionsURL());
   return true;
 }
@@ -300,8 +272,38 @@
                                  kDeviceType));
 }
 
+SuggestionsServiceImpl::SyncState SuggestionsServiceImpl::ComputeSyncState()
+    const {
+  if (!sync_service_ || !sync_service_->CanSyncStart() ||
+      sync_service_->IsLocalSyncEnabled()) {
+    return SYNC_OR_HISTORY_SYNC_DISABLED;
+  }
+  if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone()) {
+    return NOT_INITIALIZED_ENABLED;
+  }
+  return sync_service_->GetActiveDataTypes().Has(
+             syncer::HISTORY_DELETE_DIRECTIVES)
+             ? INITIALIZED_ENABLED_HISTORY
+             : SYNC_OR_HISTORY_SYNC_DISABLED;
+}
+
+bool SuggestionsServiceImpl::RefreshSyncState() {
+  SyncState new_sync_state = ComputeSyncState();
+  if (sync_state_ == new_sync_state) {
+    return false;
+  }
+  sync_state_ = new_sync_state;
+  return true;
+}
+
 void SuggestionsServiceImpl::OnStateChanged(syncer::SyncService* sync) {
-  switch (GetSyncState(sync_service_)) {
+  DCHECK(sync_service_ == sync);
+
+  if (!RefreshSyncState()) {
+    return;
+  }
+
+  switch (sync_state_) {
     case SYNC_OR_HISTORY_SYNC_DISABLED:
       // Cancel any ongoing request, to stop interacting with the server.
       pending_request_.reset(nullptr);
diff --git a/components/suggestions/suggestions_service_impl.h b/components/suggestions/suggestions_service_impl.h
index dac972d..ee0b2fd 100644
--- a/components/suggestions/suggestions_service_impl.h
+++ b/components/suggestions/suggestions_service_impl.h
@@ -87,6 +87,11 @@
  private:
   friend class SuggestionsServiceTest;
   FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, FetchSuggestionsData);
+  FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, IgnoresNoopSyncChange);
+  FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+                           IgnoresUninterestingSyncChange);
+  FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+                           FetchSuggestionsDataSyncNotInitializedEnabled);
   FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
                            FetchSuggestionsDataSyncDisabled);
   FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
@@ -104,6 +109,25 @@
   FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay);
   FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, CheckDefaultTimeStamps);
 
+  // Establishes the different sync states that matter to SuggestionsService.
+  enum SyncState {
+    // State: Sync service is not initialized, yet not disabled. History sync
+    //     state is unknown (since not initialized).
+    // Behavior: Do not issue server requests, but serve from cache if
+    //     available.
+    NOT_INITIALIZED_ENABLED,
+
+    // State: Sync service is initialized, sync is enabled and history sync is
+    //     enabled.
+    // Behavior: Update suggestions from the server on FetchSuggestionsData().
+    INITIALIZED_ENABLED_HISTORY,
+
+    // State: Sync service is disabled or history sync is disabled.
+    // Behavior: Do not issue server requests. Clear the cache. Serve empty
+    //     suggestions.
+    SYNC_OR_HISTORY_SYNC_DISABLED,
+  };
+
   // Helpers to build the various suggestions URLs. These are static members
   // rather than local functions in the .cc file to make them accessible to
   // tests.
@@ -112,6 +136,13 @@
   static GURL BuildSuggestionsBlacklistURL(const GURL& candidate_url);
   static GURL BuildSuggestionsBlacklistClearURL();
 
+  // Computes the appropriate SyncState from |sync_service_|.
+  SyncState ComputeSyncState() const;
+
+  // Re-computes |sync_state_| from the sync service. Returns whether
+  // |sync_state_| was changed.
+  bool RefreshSyncState();
+
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
 
@@ -173,6 +204,8 @@
   ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
       sync_service_observer_;
 
+  SyncState sync_state_;
+
   net::URLRequestContextGetter* url_request_context_;
 
   // The cache for the suggestions.
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc
index 217303e..2a9ab77 100644
--- a/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -339,11 +339,59 @@
   EXPECT_EQ(kTestFaviconUrl, suggestions_profile.suggestions(0).favicon_url());
 }
 
+TEST_F(SuggestionsServiceTest, IgnoresNoopSyncChange) {
+  std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
+      CreateSuggestionsServiceWithMocks());
+  auto subscription = suggestions_service->AddCallback(base::Bind(
+      &SuggestionsServiceTest::CheckCallback, base::Unretained(this)));
+
+  SuggestionsProfile suggestions_profile = CreateSuggestionsProfile();
+  factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(),
+                           suggestions_profile.SerializeAsString(),
+                           net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  // An no-op change should not result in a suggestions refresh.
+  suggestions_service->OnStateChanged(mock_sync_service_.get());
+
+  // Let any network request run (there shouldn't be one).
+  base::RunLoop().RunUntilIdle();
+
+  // Ensure that we weren't called back.
+  EXPECT_EQ(0, suggestions_data_callback_count_);
+}
+
+TEST_F(SuggestionsServiceTest, IgnoresUninterestingSyncChange) {
+  std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
+      CreateSuggestionsServiceWithMocks());
+  auto subscription = suggestions_service->AddCallback(base::Bind(
+      &SuggestionsServiceTest::CheckCallback, base::Unretained(this)));
+
+  SuggestionsProfile suggestions_profile = CreateSuggestionsProfile();
+  factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(),
+                           suggestions_profile.SerializeAsString(),
+                           net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  // An uninteresting change should not result in a network request (the
+  // SyncState is INITIALIZED_ENABLED_HISTORY before and after).
+  EXPECT_CALL(*mock_sync_service_, GetActiveDataTypes())
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(syncer::ModelTypeSet(
+          syncer::HISTORY_DELETE_DIRECTIVES, syncer::BOOKMARKS)));
+  suggestions_service->OnStateChanged(mock_sync_service_.get());
+
+  // Let any network request run (there shouldn't be one).
+  base::RunLoop().RunUntilIdle();
+
+  // Ensure that we weren't called back.
+  EXPECT_EQ(0, suggestions_data_callback_count_);
+}
+
 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
-  std::unique_ptr<SuggestionsService> suggestions_service(
+  std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_CALL(*mock_sync_service_, IsSyncActive())
       .WillRepeatedly(Return(false));
+  suggestions_service->OnStateChanged(mock_sync_service_.get());
 
   auto subscription = suggestions_service->AddCallback(base::Bind(
       &SuggestionsServiceTest::CheckCallback, base::Unretained(this)));
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 3c7f850..78376e1 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -556,6 +556,7 @@
   // The background color of the web content. This color will be drawn when the
   // web content is not able to draw in time.
   SkColor background_color_ = SK_ColorTRANSPARENT;
+  SkColor last_frame_root_background_color_ = SK_ColorTRANSPARENT;
 
   // Factory used to safely scope delayed calls to ShutdownHost().
   base::WeakPtrFactory<RenderWidgetHostViewMac> weak_factory_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 1e60855..e9ff56d 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -426,6 +426,12 @@
 }
 
 void RenderWidgetHostViewMac::AcceleratedWidgetSwapCompleted() {
+  // Set the background color for the root layer from the frame that just
+  // swapped. See RenderWidgetHostViewAura for more details. Note that this is
+  // done only after the swap has completed, so that the background is not set
+  // before the frame is up.
+  UpdateBackgroundColorFromRenderer(last_frame_root_background_color_);
+
   if (display_link_)
     display_link_->NotifyCurrentTime(base::TimeTicks::Now());
 }
@@ -1423,10 +1429,7 @@
     cc::CompositorFrame frame) {
   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
 
-  // Override the compositor background color. See RenderWidgetHostViewAura
-  // for more details.
-  UpdateBackgroundColorFromRenderer(frame.metadata.root_background_color);
-
+  last_frame_root_background_color_ = frame.metadata.root_background_color;
   last_scroll_offset_ = frame.metadata.root_scroll_offset;
 
   page_at_minimum_scale_ =
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 334aee2..a3c8d33 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -24,6 +24,8 @@
   "public/cwv_html_element.h",
   "public/cwv_navigation_action.h",
   "public/cwv_navigation_delegate.h",
+  "public/cwv_scroll_view.h",
+  "public/cwv_scroll_view_delegate.h",
   "public/cwv_translate_delegate.h",
   "public/cwv_translate_manager.h",
   "public/cwv_ui_delegate.h",
@@ -38,6 +40,8 @@
   "internal/cwv_html_element_internal.h",
   "internal/cwv_navigation_action.mm",
   "internal/cwv_navigation_action_internal.h",
+  "internal/cwv_scroll_view.mm",
+  "internal/cwv_scroll_view_internal.h",
   "internal/cwv_user_content_controller.mm",
   "internal/cwv_user_content_controller_internal.h",
   "internal/cwv_user_script.mm",
diff --git a/ios/web_view/internal/cwv_scroll_view.mm b/ios/web_view/internal/cwv_scroll_view.mm
new file mode 100644
index 0000000..53dc5d8c
--- /dev/null
+++ b/ios/web_view/internal/cwv_scroll_view.mm
@@ -0,0 +1,116 @@
+// 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/web_view/public/cwv_scroll_view.h"
+
+#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
+#import "ios/web_view/internal/cwv_scroll_view_internal.h"
+#import "ios/web_view/public/cwv_scroll_view_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface CWVScrollView ()<CRWWebViewScrollViewProxyObserver>
+
+// For KVO compliance, redefines the property as readwrite and calls its setter
+// when the value changes, instead of defining a getter which directly calls
+// _proxy.contentSize.
+@property(nonatomic, readwrite) CGSize contentSize;
+
+@end
+
+@implementation CWVScrollView
+
+@synthesize contentSize = _contentSize;
+@synthesize delegate = _delegate;
+@synthesize proxy = _proxy;
+
+- (void)setProxy:(nullable CRWWebViewScrollViewProxy*)proxy {
+  [_proxy removeObserver:self];
+  _proxy = proxy;
+  self.contentSize = _proxy.contentSize;
+  [_proxy addObserver:self];
+}
+
+- (CGPoint)contentOffset {
+  return _proxy.contentOffset;
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset {
+  _proxy.contentOffset = contentOffset;
+}
+
+- (CGRect)bounds {
+  return {_proxy.contentOffset, _proxy.frame.size};
+}
+
+- (BOOL)isDragging {
+  return _proxy.dragging;
+}
+
+- (UIEdgeInsets)contentInset {
+  return _proxy.contentInset;
+}
+
+- (void)setContentInset:(UIEdgeInsets)contentInset {
+  _proxy.contentInset = contentInset;
+}
+
+- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
+  [_proxy addGestureRecognizer:gestureRecognizer];
+}
+
+#pragma mark - NSObject
+
+- (void)dealloc {
+  // Removes |self| from |_proxy|'s observers. Otherwise |_proxy| will keep a
+  // dangling pointer to |self| and cause SEGV later.
+  [_proxy removeObserver:self];
+}
+
+#pragma mark - CRWWebViewScrollViewObserver
+
+- (void)webViewScrollViewWillBeginDragging:
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+  SEL selector = @selector(webViewScrollViewWillBeginDragging:);
+  if ([_delegate respondsToSelector:selector]) {
+    [_delegate scrollViewWillBeginDragging:self];
+  }
+}
+- (void)webViewScrollViewWillEndDragging:
+            (CRWWebViewScrollViewProxy*)webViewScrollViewProxy
+                            withVelocity:(CGPoint)velocity
+                     targetContentOffset:(inout CGPoint*)targetContentOffset {
+  SEL selector = @selector
+      (webViewScrollViewWillEndDragging:withVelocity:targetContentOffset:);
+  if ([_delegate respondsToSelector:selector]) {
+    [_delegate scrollViewWillEndDragging:self
+                            withVelocity:velocity
+                     targetContentOffset:targetContentOffset];
+  }
+}
+
+- (void)webViewScrollViewDidScroll:
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+  SEL selector = @selector(scrollViewDidScroll:);
+  if ([_delegate respondsToSelector:selector]) {
+    [_delegate scrollViewDidScroll:self];
+  }
+}
+
+- (void)webViewScrollViewDidEndDecelerating:
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+  SEL selector = @selector(webViewScrollViewDidEndDecelerating:);
+  if ([_delegate respondsToSelector:selector]) {
+    [_delegate scrollViewDidEndDecelerating:self];
+  }
+}
+
+- (void)webViewScrollViewDidResetContentSize:
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+  self.contentSize = _proxy.contentSize;
+}
+
+@end
diff --git a/ios/web_view/internal/cwv_scroll_view_internal.h b/ios/web_view/internal/cwv_scroll_view_internal.h
new file mode 100644
index 0000000..5328719
--- /dev/null
+++ b/ios/web_view/internal/cwv_scroll_view_internal.h
@@ -0,0 +1,22 @@
+// 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_WEB_VIEW_INTERNAL_CWV_SCROLL_VIEW_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_CWV_SCROLL_VIEW_INTERNAL_H_
+
+#import <ChromeWebView/cwv_export.h>
+#import <Foundation/Foundation.h>
+
+#import "ios/web_view/public/cwv_scroll_view.h"
+
+@class CRWWebViewScrollViewProxy;
+
+@interface CWVScrollView ()
+
+// Operations to this scroll view are delegated to this proxy.
+@property(nonatomic, weak, readwrite) CRWWebViewScrollViewProxy* proxy;
+
+@end
+
+#endif  // IOS_WEB_VIEW_INTERNAL_CWV_SCROLL_VIEW_INTERNAL_H_
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 503cadd..544a327 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -17,11 +17,14 @@
 #import "ios/web/public/web_state/context_menu_params.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/ui/crw_web_delegate.h"
+#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
+#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_delegate_bridge.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #import "ios/web_view/internal/cwv_html_element_internal.h"
 #import "ios/web_view/internal/cwv_navigation_action_internal.h"
+#import "ios/web_view/internal/cwv_scroll_view_internal.h"
 #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
 #import "ios/web_view/internal/translate/web_view_translate_client.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
@@ -85,6 +88,7 @@
 @synthesize translationDelegate = _translationDelegate;
 @synthesize estimatedProgress = _estimatedProgress;
 @synthesize UIDelegate = _UIDelegate;
+@synthesize scrollView = _scrollView;
 
 + (NSString*)userAgentProduct {
   return gUserAgentProduct;
@@ -114,6 +118,7 @@
   self = [super initWithFrame:frame];
   if (self) {
     _configuration = [configuration copy];
+    _scrollView = [[CWVScrollView alloc] init];
     [self resetWebStateWithSessionStorage:nil];
   }
   return self;
@@ -341,6 +346,8 @@
       base::MakeUnique<ios_web_view::WebViewJavaScriptDialogPresenter>(self,
                                                                        nullptr);
 
+  _scrollView.proxy = _webState.get()->GetWebViewProxy().scrollViewProxy;
+
   // Initialize Translate.
   ios_web_view::WebViewTranslateClient::CreateForWebState(_webState.get());
 
diff --git a/ios/web_view/public/ChromeWebView.h b/ios/web_view/public/ChromeWebView.h
index 9dc9408..4b2a1358 100644
--- a/ios/web_view/public/ChromeWebView.h
+++ b/ios/web_view/public/ChromeWebView.h
@@ -9,6 +9,8 @@
 #import <ChromeWebView/cwv_html_element.h>
 #import <ChromeWebView/cwv_navigation_action.h>
 #import <ChromeWebView/cwv_navigation_delegate.h>
+#import <ChromeWebView/cwv_scroll_view.h>
+#import <ChromeWebView/cwv_scroll_view_delegate.h>
 #import <ChromeWebView/cwv_translate_delegate.h>
 #import <ChromeWebView/cwv_translate_manager.h>
 #import <ChromeWebView/cwv_ui_delegate.h>
diff --git a/ios/web_view/public/cwv_scroll_view.h b/ios/web_view/public/cwv_scroll_view.h
new file mode 100644
index 0000000..4b5c268f
--- /dev/null
+++ b/ios/web_view/public/cwv_scroll_view.h
@@ -0,0 +1,47 @@
+// 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_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_H_
+
+#import <ChromeWebView/cwv_export.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@protocol CWVScrollViewDelegate;
+
+// Scroll view inside the web view. This is not a subclass of UIScrollView
+// because the underlying //ios/web API only exposes a proxy object of the
+// scroll view, not the raw UIScrollView.
+//
+// These methods are forwarded to the internal UIScrollView. Please see the
+// <UIKit/UIScrollView.h> documentation for details about the following methods.
+//
+// TODO(crbug.com/719323): Add nullability annotations.
+CWV_EXPORT
+@interface CWVScrollView : NSObject
+
+@property(nonatomic, readonly) CGRect bounds;
+@property(nonatomic) CGPoint contentOffset;
+@property(nonatomic, weak) id<CWVScrollViewDelegate> delegate;
+@property(nonatomic, readonly, getter=isDragging) BOOL dragging;
+
+// KVO compliant.
+@property(nonatomic, readonly) CGSize contentSize;
+
+// Be careful when using this property. There's a bug with the
+// underlying WKWebView where the web view does not respect contentInsets
+// properly when laying out content and calculating innerHeight for Javascript.
+// Content is laid out based on the entire height of the web view rather than
+// the height between the top and bottom insets.
+// https://bugs.webkit.org/show_bug.cgi?id=134230
+// rdar://23584409 (not available on Open Radar)
+@property(nonatomic) UIEdgeInsets contentInset;
+
+- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
+
+@end
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_H_
diff --git a/ios/web_view/public/cwv_scroll_view_delegate.h b/ios/web_view/public/cwv_scroll_view_delegate.h
new file mode 100644
index 0000000..d5eee58
--- /dev/null
+++ b/ios/web_view/public/cwv_scroll_view_delegate.h
@@ -0,0 +1,28 @@
+// 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_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_DELEGATE_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_DELEGATE_H_
+
+#import <CoreGraphics/CoreGraphics.h>
+
+@class CWVScrollView;
+
+// Delegete for CWVScrollView.
+//
+// These methods are forwarded from the internal UIScrollViewDelegate. Please
+// see the <UIKit/UIScrollViewDelegate.h> documentation for details about the
+// following methods.
+CWV_EXPORT
+@protocol CWVScrollViewDelegate<NSObject>
+@optional
+- (void)scrollViewWillBeginDragging:(CWVScrollView*)scrollView;
+- (void)scrollViewWillEndDragging:(CWVScrollView*)scrollView
+                     withVelocity:(CGPoint)velocity
+              targetContentOffset:(inout CGPoint*)targetContentOffset;
+- (void)scrollViewDidScroll:(CWVScrollView*)scrollView;
+- (void)scrollViewDidEndDecelerating:(CWVScrollView*)scrollView;
+@end
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_SCROLL_VIEW_DELEGATE_H_
diff --git a/ios/web_view/public/cwv_web_view.h b/ios/web_view/public/cwv_web_view.h
index 639a85c..3bf3ee6 100644
--- a/ios/web_view/public/cwv_web_view.h
+++ b/ios/web_view/public/cwv_web_view.h
@@ -7,6 +7,7 @@
 #import <ChromeWebView/cwv_export.h>
 #import <UIKit/UIKit.h>
 
+@class CWVScrollView;
 @class CWVWebViewConfiguration;
 @protocol CWVUIDelegate;
 @protocol CWVTranslateDelegate;
@@ -57,6 +58,9 @@
 // is reset to 0.0.
 @property(nonatomic, readonly) double estimatedProgress;
 
+// The scroll view associated with the web view.
+@property(nonatomic, readonly) CWVScrollView* scrollView;
+
 // The User Agent product string used to build the full User Agent.
 + (NSString*)userAgentProduct;
 
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index c7b1b2a..da1433f 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -203,6 +203,17 @@
                                          [_webView reload];
                                        }]];
 
+  // Removes the web view from the view hierarchy and deallocates it. For
+  // testing deallocation behavior, because there have been multiple crash bugs
+  // on deallocation of CWVWebView.
+  [alertController
+      addAction:[UIAlertAction actionWithTitle:@"Deallocate web view"
+                                         style:UIAlertActionStyleDefault
+                                       handler:^(UIAlertAction* action) {
+                                         [_webView removeFromSuperview];
+                                         _webView = nil;
+                                       }]];
+
   [self presentViewController:alertController animated:YES completion:nil];
 }
 
diff --git a/services/catalog/catalog.cc b/services/catalog/catalog.cc
index 0110b7b..beb8d177 100644
--- a/services/catalog/catalog.cc
+++ b/services/catalog/catalog.cc
@@ -102,7 +102,7 @@
     auto entry = Entry::Deserialize(*manifest);
     if (entry) {
       if (!executable_path.empty())
-        entry->set_path(executable_path);
+        entry->set_path(std::move(executable_path));
       bool added = cache->AddRootEntry(std::move(entry));
       DCHECK(added);
     } else {
diff --git a/services/catalog/entry.cc b/services/catalog/entry.cc
index 2e2836bd..90f61531 100644
--- a/services/catalog/entry.cc
+++ b/services/catalog/entry.cc
@@ -29,7 +29,7 @@
       LOG(ERROR) << "Entry::Deserialize: list member must be a string";
       return false;
     }
-    string_set->insert(value);
+    string_set->insert(std::move(value));
   }
   return true;
 }
@@ -44,14 +44,23 @@
   return ReadStringSet(*list_value, string_set);
 }
 
+// If |key| refers to a dictionary value within |value|, |*out| is set to that
+// DictionaryValue. Returns true if either |key| is not present or the
+// corresponding value is a dictionary.
+bool GetDictionaryValue(const base::DictionaryValue& value,
+                        base::StringPiece key,
+                        const base::DictionaryValue** out) {
+  const base::Value* entry_value = nullptr;
+  return !value.Get(key, &entry_value) || entry_value->GetAsDictionary(out);
+}
+
 bool BuildInterfaceProviderSpec(
     const base::DictionaryValue& value,
     service_manager::InterfaceProviderSpec* interface_provider_specs) {
   DCHECK(interface_provider_specs);
   const base::DictionaryValue* provides_value = nullptr;
-  if (value.HasKey(Store::kInterfaceProviderSpecs_ProvidesKey) &&
-      !value.GetDictionary(Store::kInterfaceProviderSpecs_ProvidesKey,
-                           &provides_value)) {
+  if (!GetDictionaryValue(value, Store::kInterfaceProviderSpecs_ProvidesKey,
+                          &provides_value)) {
     LOG(ERROR) << "Entry::Deserialize: "
                << Store::kInterfaceProviderSpecs_ProvidesKey
                << " must be a dictionary.";
@@ -66,14 +75,13 @@
                    << " capabilities dictionary";
         return false;
       }
-      interface_provider_specs->provides[it.key()] = interfaces;
+      interface_provider_specs->provides[it.key()] = std::move(interfaces);
     }
   }
 
   const base::DictionaryValue* requires_value = nullptr;
-  if (value.HasKey(Store::kInterfaceProviderSpecs_RequiresKey) &&
-      !value.GetDictionary(Store::kInterfaceProviderSpecs_RequiresKey,
-                           &requires_value)) {
+  if (!GetDictionaryValue(value, Store::kInterfaceProviderSpecs_RequiresKey,
+                          &requires_value)) {
     LOG(ERROR) << "Entry::Deserialize: "
                << Store::kInterfaceProviderSpecs_RequiresKey
                << " must be a dictionary.";
@@ -96,7 +104,7 @@
         return false;
       }
 
-      interface_provider_specs->requires[it.key()] = capabilities;
+      interface_provider_specs->requires[it.key()] = std::move(capabilities);
     }
   }
   return true;
@@ -130,14 +138,15 @@
     LOG(ERROR) << "Entry::Deserialize: empty service name.";
     return nullptr;
   }
-  entry->set_name(name);
+  entry->set_name(std::move(name));
 
   // By default we assume a standalone service executable. The catalog may
   // override this layer based on configuration external to the service's own
   // manifest.
   base::FilePath module_path;
   base::PathService::Get(base::DIR_MODULE, &module_path);
-  entry->set_path(module_path.AppendASCII(name + kServiceExecutableExtension));
+  entry->set_path(
+      module_path.AppendASCII(entry->name() + kServiceExecutableExtension));
 
   // Human-readable name.
   std::string display_name;
@@ -146,7 +155,7 @@
                << Store::kDisplayNameKey << " key";
     return nullptr;
   }
-  entry->set_display_name(display_name);
+  entry->set_display_name(std::move(display_name));
 
   // InterfaceProvider specs.
   const base::DictionaryValue* interface_provider_specs = nullptr;
@@ -172,20 +181,19 @@
                  << "spec for key: " << it.key();
       return nullptr;
     }
-    entry->AddInterfaceProviderSpec(it.key(), spec);
+    entry->AddInterfaceProviderSpec(it.key(), std::move(spec));
   }
 
   // Required files.
   base::Optional<RequiredFileMap> required_files =
       catalog::RetrieveRequiredFiles(value);
   DCHECK(required_files);
-  for (const auto& iter : *required_files) {
-    entry->AddRequiredFilePath(iter.first, iter.second);
+  for (auto& iter : *required_files) {
+    entry->AddRequiredFilePath(iter.first, std::move(iter.second));
   }
 
-  if (value.HasKey(Store::kServicesKey)) {
-    const base::ListValue* services = nullptr;
-    value.GetList(Store::kServicesKey, &services);
+  const base::ListValue* services = nullptr;
+  if (value.GetList(Store::kServicesKey, &services)) {
     for (size_t i = 0; i < services->GetSize(); ++i) {
       const base::DictionaryValue* service = nullptr;
       services->GetDictionary(i, &service);
@@ -206,7 +214,7 @@
   if (it == interface_provider_specs_.end())
     return false;
 
-  auto connection_spec = it->second;
+  const auto& connection_spec = it->second;
   return connection_spec.provides.find(capability) !=
       connection_spec.provides.end();
 }
@@ -219,13 +227,12 @@
 
 void Entry::AddInterfaceProviderSpec(
     const std::string& name,
-    const service_manager::InterfaceProviderSpec& spec) {
-  interface_provider_specs_[name] = spec;
+    service_manager::InterfaceProviderSpec spec) {
+  interface_provider_specs_[name] = std::move(spec);
 }
 
-void Entry::AddRequiredFilePath(const std::string& name,
-                                const base::FilePath& path) {
-  required_file_paths_[name] = path;
+void Entry::AddRequiredFilePath(const std::string& name, base::FilePath path) {
+  required_file_paths_[name] = std::move(path);
 }
 
 }  // catalog
diff --git a/services/catalog/entry.h b/services/catalog/entry.h
index f7332d8..21ba2d3 100644
--- a/services/catalog/entry.h
+++ b/services/catalog/entry.h
@@ -35,14 +35,14 @@
   bool operator==(const Entry& other) const;
 
   const std::string& name() const { return name_; }
-  void set_name(const std::string& name) { name_ = name; }
+  void set_name(std::string name) { name_ = std::move(name); }
 
   const base::FilePath& path() const { return path_; }
-  void set_path(const base::FilePath& path) { path_ = path; }
+  void set_path(base::FilePath path) { path_ = std::move(path); }
 
   const std::string& display_name() const { return display_name_; }
-  void set_display_name(const std::string& display_name) {
-    display_name_ = display_name;
+  void set_display_name(std::string display_name) {
+    display_name_ = std::move(display_name);
   }
 
   const Entry* parent() const { return parent_; }
@@ -52,19 +52,18 @@
     return children_;
   }
   std::vector<std::unique_ptr<Entry>>& children() { return children_; }
-  void set_children(std::vector<std::unique_ptr<Entry>>&& children) {
+  void set_children(std::vector<std::unique_ptr<Entry>> children) {
     children_ = std::move(children);
   }
 
-  void AddInterfaceProviderSpec(
-      const std::string& name,
-      const service_manager::InterfaceProviderSpec& spec);
+  void AddInterfaceProviderSpec(const std::string& name,
+                                service_manager::InterfaceProviderSpec spec);
   const service_manager::InterfaceProviderSpecMap&
       interface_provider_specs() const {
     return interface_provider_specs_;
   }
 
-  void AddRequiredFilePath(const std::string& name, const base::FilePath& path);
+  void AddRequiredFilePath(const std::string& name, base::FilePath path);
   const std::map<std::string, base::FilePath>& required_file_paths() const {
     return required_file_paths_;
   }
diff --git a/services/service_manager/public/cpp/interface_provider_spec.cc b/services/service_manager/public/cpp/interface_provider_spec.cc
index e9b6a8c5..06078b6c 100644
--- a/services/service_manager/public/cpp/interface_provider_spec.cc
+++ b/services/service_manager/public/cpp/interface_provider_spec.cc
@@ -13,8 +13,15 @@
 InterfaceProviderSpec::InterfaceProviderSpec() {}
 InterfaceProviderSpec::InterfaceProviderSpec(
     const InterfaceProviderSpec& other) = default;
+InterfaceProviderSpec::InterfaceProviderSpec(InterfaceProviderSpec&& other) =
+    default;
 InterfaceProviderSpec::~InterfaceProviderSpec() {}
 
+InterfaceProviderSpec& InterfaceProviderSpec::operator=(
+    const InterfaceProviderSpec& other) = default;
+InterfaceProviderSpec& InterfaceProviderSpec::operator=(
+    InterfaceProviderSpec&& other) = default;
+
 bool InterfaceProviderSpec::operator==(
     const InterfaceProviderSpec& other) const {
   return other.provides == provides && other.requires == requires;
diff --git a/services/service_manager/public/cpp/interface_provider_spec.h b/services/service_manager/public/cpp/interface_provider_spec.h
index 4d6e45b..7864cba 100644
--- a/services/service_manager/public/cpp/interface_provider_spec.h
+++ b/services/service_manager/public/cpp/interface_provider_spec.h
@@ -24,7 +24,10 @@
 struct InterfaceProviderSpec {
   InterfaceProviderSpec();
   InterfaceProviderSpec(const InterfaceProviderSpec& other);
+  InterfaceProviderSpec(InterfaceProviderSpec&& other);
   ~InterfaceProviderSpec();
+  InterfaceProviderSpec& operator=(const InterfaceProviderSpec& other);
+  InterfaceProviderSpec& operator=(InterfaceProviderSpec&& other);
   bool operator==(const InterfaceProviderSpec& other) const;
   bool operator<(const InterfaceProviderSpec& other) const;
   std::map<Capability, InterfaceSet> provides;
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc
index 5cecb4a2..f013fc3 100644
--- a/services/service_manager/service_manager.cc
+++ b/services/service_manager/service_manager.cc
@@ -94,21 +94,26 @@
   return exposed_interfaces;
 }
 
-}  // namespace
-
-Identity CreateServiceManagerIdentity() {
-  return Identity(service_manager::mojom::kServiceName, mojom::kRootUserID);
-}
-
 Identity CreateCatalogIdentity() {
   return Identity(catalog::mojom::kServiceName, mojom::kRootUserID);
 }
 
-InterfaceProviderSpec GetPermissiveInterfaceProviderSpec() {
+InterfaceProviderSpec CreatePermissiveInterfaceProviderSpec() {
   InterfaceProviderSpec spec;
   InterfaceSet interfaces;
   interfaces.insert("*");
-  spec.requires["*"] = interfaces;
+  spec.requires["*"] = std::move(interfaces);
+  return spec;
+}
+
+const InterfaceProviderSpec& GetPermissiveInterfaceProviderSpec() {
+  CR_DEFINE_STATIC_LOCAL(InterfaceProviderSpec, spec,
+                         (CreatePermissiveInterfaceProviderSpec()));
+  return spec;
+}
+
+const InterfaceProviderSpec& GetEmptyInterfaceProviderSpec() {
+  CR_DEFINE_STATIC_LOCAL(InterfaceProviderSpec, spec, ());
   return spec;
 }
 
@@ -139,6 +144,12 @@
   return allowed;
 }
 
+}  // namespace
+
+Identity CreateServiceManagerIdentity() {
+  return Identity(service_manager::mojom::kServiceName, mojom::kRootUserID);
+}
+
 // Encapsulates a connection to an instance of a service, tracked by the
 // Service Manager.
 class ServiceManager::Instance
@@ -150,11 +161,11 @@
  public:
   Instance(service_manager::ServiceManager* service_manager,
            const Identity& identity,
-           const InterfaceProviderSpecMap& interface_provider_specs)
+           InterfaceProviderSpecMap interface_provider_specs)
       : service_manager_(service_manager),
         id_(GenerateUniqueID()),
         identity_(identity),
-        interface_provider_specs_(interface_provider_specs),
+        interface_provider_specs_(std::move(interface_provider_specs)),
         allow_any_application_(GetConnectionSpec().requires.count("*") == 1),
         pid_receiver_binding_(this),
         control_binding_(this),
@@ -198,14 +209,10 @@
     }
 
     std::unique_ptr<ConnectParams> params(std::move(*in_params));
-    InterfaceProviderSpecMap source_specs;
-    InterfaceProviderSpec source_connection_spec;
     Instance* source =
         service_manager_->GetExistingInstance(params->source());
-    if (source) {
-      source_specs = source->interface_provider_specs_;
-      source_connection_spec = source->GetConnectionSpec();
-    }
+    const InterfaceProviderSpec& source_connection_spec =
+        source ? source->GetConnectionSpec() : GetEmptyInterfaceProviderSpec();
 
     if (!AllowsInterface(params->source(), source_connection_spec, identity_,
                          GetConnectionSpec(), params->interface_name())) {
@@ -276,7 +283,9 @@
   }
   const InterfaceProviderSpec& GetSpec(const std::string& spec) const {
     auto it = interface_provider_specs_.find(spec);
-    return it != interface_provider_specs_.end() ? it->second : empty_spec_;
+    return it != interface_provider_specs_.end()
+               ? it->second
+               : GetEmptyInterfaceProviderSpec();
   }
 
   const Identity& identity() const { return identity_; }
@@ -517,7 +526,7 @@
   }
 
   mojom::ConnectResult ValidateConnectionSpec(const Identity& target) {
-    InterfaceProviderSpec connection_spec = GetConnectionSpec();
+    const InterfaceProviderSpec& connection_spec = GetConnectionSpec();
     // TODO(beng): Need to do the following additional policy validation of
     // whether this instance is allowed to connect using:
     // - non-null client process info.
@@ -614,7 +623,6 @@
   const uint32_t id_;
   Identity identity_;
   const InterfaceProviderSpecMap interface_provider_specs_;
-  const InterfaceProviderSpec empty_spec_;
   const bool allow_any_application_;
   std::unique_ptr<ServiceProcessLauncher> runner_;
   mojom::ServicePtr service_;
@@ -702,10 +710,10 @@
   spec.requires[catalog::mojom::kServiceName].insert(
       "service_manager:resolver");
   InterfaceProviderSpecMap specs;
-  specs[mojom::kServiceManager_ConnectorSpec] = spec;
+  specs[mojom::kServiceManager_ConnectorSpec] = std::move(spec);
 
-  service_manager_instance_ =
-      CreateInstance(Identity(), CreateServiceManagerIdentity(), specs);
+  service_manager_instance_ = CreateInstance(
+      Identity(), CreateServiceManagerIdentity(), std::move(specs));
   service_manager_instance_->StartWithService(std::move(service));
   singletons_.insert(service_manager::mojom::kServiceName);
   service_context_.reset(new ServiceContext(
@@ -743,7 +751,7 @@
 
 void ServiceManager::SetInstanceQuitCallback(
     base::Callback<void(const Identity&)> callback) {
-  instance_quit_callback_ = callback;
+  instance_quit_callback_ = std::move(callback);
 }
 
 void ServiceManager::Connect(std::unique_ptr<ConnectParams> params) {
@@ -794,10 +802,11 @@
       "service_manager::mojom::Resolver");
   spec.provides["control"].insert("catalog::mojom::CatalogControl");
   InterfaceProviderSpecMap specs;
-  specs[mojom::kServiceManager_ConnectorSpec] = spec;
+  specs[mojom::kServiceManager_ConnectorSpec] = std::move(spec);
 
-  Instance* instance = CreateInstance(
-      CreateServiceManagerIdentity(), CreateCatalogIdentity(), specs);
+  Instance* instance =
+      CreateInstance(CreateServiceManagerIdentity(), CreateCatalogIdentity(),
+                     std::move(specs));
   singletons_.insert(catalog::mojom::kServiceName);
   instance->StartWithService(std::move(catalog));
 }
@@ -941,10 +950,10 @@
 ServiceManager::Instance* ServiceManager::CreateInstance(
     const Identity& source,
     const Identity& target,
-    const InterfaceProviderSpecMap& specs) {
+    InterfaceProviderSpecMap specs) {
   CHECK(target.user_id() != mojom::kInheritUserID);
 
-  auto instance = base::MakeUnique<Instance>(this, target, specs);
+  auto instance = base::MakeUnique<Instance>(this, target, std::move(specs));
   Instance* raw_instance = instance.get();
 
   instances_.insert(std::make_pair(raw_instance, std::move(instance)));
@@ -1025,14 +1034,15 @@
     return;
   }
 
-  std::string instance_name = params->target().instance();
+  const std::string& instance_name = params->target().instance();
 
   // |result->interface_provider_specs| can be empty when there is no manifest.
-  InterfaceProviderSpec connection_spec = GetPermissiveInterfaceProviderSpec();
   auto it = result->interface_provider_specs.find(
       mojom::kServiceManager_ConnectorSpec);
-  if (it != result->interface_provider_specs.end())
-    connection_spec = it->second;
+  const InterfaceProviderSpec& connection_spec =
+      it != result->interface_provider_specs.end()
+          ? it->second
+          : GetPermissiveInterfaceProviderSpec();
 
   const Identity original_target(params->target());
   const std::string user_id =
@@ -1047,8 +1057,6 @@
   if (ConnectToExistingInstance(&params))
     return;
 
-  Identity source = params->source();
-
   // Services that request "all_users" class from the Service Manager are
   // allowed to field connection requests from any user. They also run with a
   // synthetic user id generated here. The user id provided via Connect() is
@@ -1063,8 +1071,11 @@
     source_identity_for_creation = params->source();
   }
 
-  Instance* instance = CreateInstance(source_identity_for_creation,
-                                      target, result->interface_provider_specs);
+  bool result_interface_provider_specs_empty =
+      result->interface_provider_specs.empty();
+  Instance* instance =
+      CreateInstance(source_identity_for_creation, target,
+                     std::move(result->interface_provider_specs));
 
   // Below are various paths through which a new Instance can be bound to a
   // Service proxy.
@@ -1084,7 +1095,7 @@
     // anything more.
     // TODO(beng): There may be some cases where it's valid to have an empty
     // spec, so we should probably include a return value in |result|.
-    if (result->interface_provider_specs.empty()) {
+    if (result_interface_provider_specs_empty) {
       LOG(ERROR)
           << "Error: The catalog was unable to read a manifest for service \""
           << result->name << "\".";
@@ -1095,7 +1106,7 @@
 
     if (parent) {
       // This service is provided by another service via a ServiceFactory.
-      std::string target_user_id = target.user_id();
+      const std::string* target_user_id = &target.user_id();
       std::string factory_instance_name = instance_name;
 
       auto spec_iter = parent->interface_provider_specs.find(
@@ -1108,20 +1119,20 @@
       } else {
         // Use the original user ID so the existing embedder factory can
         // be found and used to create the new service.
-        target_user_id = original_target.user_id();
+        target_user_id = &original_target.user_id();
         Identity packaged_service_target(target);
         packaged_service_target.set_user_id(original_target.user_id());
         instance->set_identity(packaged_service_target);
       }
       instance->StartWithService(std::move(service));
 
-      Identity factory(parent->name, target_user_id, factory_instance_name);
+      Identity factory(parent->name, *target_user_id, factory_instance_name);
       CreateServiceWithFactory(factory, target.name(), std::move(request));
     } else {
       base::FilePath package_path;
       if (!service_overrides_ || !service_overrides_->GetExecutablePathOverride(
             target.name(), &package_path)) {
-        package_path = result->package_path;
+        package_path = std::move(result->package_path);
       }
       DCHECK(!package_path.empty());
 
diff --git a/services/service_manager/service_manager.h b/services/service_manager/service_manager.h
index 0fc8d5d..51969321 100644
--- a/services/service_manager/service_manager.h
+++ b/services/service_manager/service_manager.h
@@ -135,7 +135,7 @@
 
   Instance* CreateInstance(const Identity& source,
                            const Identity& target,
-                           const InterfaceProviderSpecMap& specs);
+                           InterfaceProviderSpecMap specs);
 
   // Called from the instance implementing mojom::ServiceManager.
   void AddListener(mojom::ServiceManagerListenerPtr listener);
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ee24cc7..b39df83a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1606,6 +1606,24 @@
             ]
         }
     ],
+    "NTPTilesInInstantService": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "NTPTilesInInstantService"
+                    ]
+                }
+            ]
+        }
+    ],
     "NetDelayableH2AndQuicRequests": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-invalidation-logging.js b/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-invalidation-logging.js
index f1398344..06110c9 100644
--- a/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-invalidation-logging.js
+++ b/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-invalidation-logging.js
@@ -32,7 +32,7 @@
         }
     `;
 
-    paintWorklet.addModule(URL.createObjectURL(new Blob([workletCode]))).then(function() {
+    paintWorklet.addModule(URL.createObjectURL(new Blob([workletCode], {type: 'text/javascript'}))).then(function() {
         for (let i = 0; i < tests.length; i++) {
             tests[i].paintName = 'paint-' + i;
             registerTest(imageType, tests[i]);
diff --git a/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-paint-worklet.js b/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-paint-worklet.js
index bb1dfef..2813491 100644
--- a/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-paint-worklet.js
+++ b/third_party/WebKit/LayoutTests/csspaint/resources/test-runner-paint-worklet.js
@@ -9,7 +9,7 @@
       testRunner.waitUntilDone();
     }
 
-    var blob = new Blob([code]);
+    var blob = new Blob([code], {type: 'text/javascript'});
     paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
         runAfterLayoutAndPaint(function() {
             if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/csspaint/valid-image-after-load.html b/third_party/WebKit/LayoutTests/csspaint/valid-image-after-load.html
index 7bb5bcd..ed9f0cc 100644
--- a/third_party/WebKit/LayoutTests/csspaint/valid-image-after-load.html
+++ b/third_party/WebKit/LayoutTests/csspaint/valid-image-after-load.html
@@ -27,7 +27,8 @@
   testRunner.waitUntilDone();
 }
 
-var blob = new Blob([document.getElementById('code').textContent]);
+var blob = new Blob([document.getElementById('code').textContent],
+                    {type: 'text/javascript'});
 paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
     var el = document.getElementById('output');
     el.style.backgroundImage = 'paint(green)';
diff --git a/third_party/WebKit/LayoutTests/csspaint/valid-image-before-load.html b/third_party/WebKit/LayoutTests/csspaint/valid-image-before-load.html
index 3a6eacf..3e6f37b2 100644
--- a/third_party/WebKit/LayoutTests/csspaint/valid-image-before-load.html
+++ b/third_party/WebKit/LayoutTests/csspaint/valid-image-before-load.html
@@ -30,7 +30,8 @@
 var el = document.getElementById('output');
 el.style.backgroundImage = 'paint(green)';
 
-var blob = new Blob([document.getElementById('code').textContent]);
+var blob = new Blob([document.getElementById('code').textContent],
+                    {type: 'text/javascript'});
 paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
     runAfterLayoutAndPaint(function() {
         if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worklet-import-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worklet-import-blocked-expected.txt
index 74af2b2..76df14a4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worklet-import-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worklet-import-blocked-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 13: Refused to load the script 'http://127.0.0.1:8000/resources/worklet.js' because it violates the following Content Security Policy directive: "script-src 'unsafe-inline'".
+CONSOLE ERROR: Refused to load the script 'http://127.0.0.1:8000/resources/worklet.js' because it violates the following Content Security Policy directive: "script-src 'unsafe-inline'".
 
 This is a testharness.js-based test.
 PASS Importing a script rejects the given promise with NetworkError because it violated the same origin CSP policy. 
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 5956fd1..7076913 100644
--- a/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
@@ -33,6 +33,11 @@
 
 namespace blink {
 
+{% for subgroup in computed_style.subgroups %}
+{{define_field_group_class(subgroup)|indent(2)}}
+
+{% endfor %}
+
 // The generated portion of ComputedStyle. For more info, see the header comment
 // in ComputedStyle.h.
 class CORE_EXPORT ComputedStyleBase {
@@ -104,11 +109,6 @@
   {{field_templates[field.field_template].decl_public_methods(field)|indent(2)}}
 
   {% endfor %}
- private:
-  {% for subgroup in computed_style.subgroups %}
-  {{define_field_group_class(subgroup)|indent(2)}}
-
-  {% endfor %}
 
  protected:
   // Constructor and destructor are protected so that only the parent class ComputedStyle
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 6d3486c..e16dbde3 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -545,6 +545,10 @@
       custom_all: true,
       is_descriptor: true,
       priority: "High",
+      field_template: "storage_only",
+      type_name: "float",
+      default_value: "1.0",
+      field_group: "visual",
     },
 
     {
@@ -892,6 +896,10 @@
       converter: "ConvertClip",
       custom_all: true,
       interpolable: true,
+      field_template: "storage_only",
+      field_type_path: "platform/LengthBox",
+      default_value: "LengthBox()",
+      field_group: "visual",
     },
     {
       name: "clip-path",
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
index 68e408a..5e7fae35 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
+++ b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
@@ -183,5 +183,21 @@
       default_value: "Color::kTransparent",
       field_group: "background",
     },
+    {
+      name: "TextDecoration",
+      field_template: "storage_only",
+      type_name: "TextDecoration",
+      field_size: 4,
+      default_value: "kTextDecorationNone",
+      field_group: "visual",
+    },
+    {
+      name: "HasAutoClip",
+      field_template: "storage_only",
+      type_name: "bool",
+      field_size: 1,
+      default_value: "true",
+      field_group: "visual",
+    },
   ],
 }
diff --git a/third_party/WebKit/Source/core/dom/Modulator.cpp b/third_party/WebKit/Source/core/dom/Modulator.cpp
index e0b2ed2..83a1157 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.cpp
+++ b/third_party/WebKit/Source/core/dom/Modulator.cpp
@@ -29,7 +29,7 @@
       static_cast<Modulator*>(per_context_data->GetData(kPerContextDataKey));
   if (!modulator) {
     if (Document* document = ToDocument(ExecutionContext::From(script_state))) {
-      modulator = ModulatorImpl::Create(script_state, *document);
+      modulator = ModulatorImpl::Create(script_state, document->Fetcher());
       Modulator::SetModulator(script_state, modulator);
     }
   }
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
index e5e4f35d..16081ff 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
@@ -4,7 +4,6 @@
 
 #include "core/dom/ModulatorImpl.h"
 
-#include "core/dom/Document.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/dom/ModuleMap.h"
 #include "core/dom/ModuleScript.h"
@@ -19,18 +18,15 @@
 namespace blink {
 
 ModulatorImpl* ModulatorImpl::Create(RefPtr<ScriptState> script_state,
-                                     Document& document) {
-  return new ModulatorImpl(
-      std::move(script_state),
-      TaskRunnerHelper::Get(TaskType::kNetworking, &document),
-      document.Fetcher());
+                                     ResourceFetcher* resource_fetcher) {
+  return new ModulatorImpl(std::move(script_state), resource_fetcher);
 }
 
 ModulatorImpl::ModulatorImpl(RefPtr<ScriptState> script_state,
-                             RefPtr<WebTaskRunner> task_runner,
                              ResourceFetcher* fetcher)
     : script_state_(std::move(script_state)),
-      task_runner_(std::move(task_runner)),
+      task_runner_(
+          TaskRunnerHelper::Get(TaskType::kNetworking, script_state_.Get())),
       fetcher_(fetcher),
       map_(this, ModuleMap::Create(this)),
       loader_registry_(ModuleScriptLoaderRegistry::Create()),
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImpl.h b/third_party/WebKit/Source/core/dom/ModulatorImpl.h
index b4f2afd..c971e76 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImpl.h
+++ b/third_party/WebKit/Source/core/dom/ModulatorImpl.h
@@ -14,7 +14,6 @@
 
 namespace blink {
 
-class Document;
 class ExecutionContext;
 class ModuleMap;
 class ModuleScriptLoaderRegistry;
@@ -29,7 +28,7 @@
 // components together.
 class ModulatorImpl final : public Modulator {
  public:
-  static ModulatorImpl* Create(RefPtr<ScriptState>, Document&);
+  static ModulatorImpl* Create(RefPtr<ScriptState>, ResourceFetcher*);
 
   virtual ~ModulatorImpl();
   DECLARE_TRACE();
@@ -67,7 +66,7 @@
   Vector<String> ModuleRequestsFromScriptModule(ScriptModule) override;
   void ExecuteModule(const ModuleScript*) override;
 
-  ModulatorImpl(RefPtr<ScriptState>, RefPtr<WebTaskRunner>, ResourceFetcher*);
+  ModulatorImpl(RefPtr<ScriptState>, ResourceFetcher*);
 
   ExecutionContext* GetExecutionContext() const;
 
diff --git a/third_party/WebKit/Source/core/editing/DOMSelection.cpp b/third_party/WebKit/Source/core/editing/DOMSelection.cpp
index 6e31518b..dedd3cb0 100644
--- a/third_party/WebKit/Source/core/editing/DOMSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/DOMSelection.cpp
@@ -369,12 +369,6 @@
 
   ClearCachedRangeIfSelectionOfDocument();
 
-  // TODO(editing-dev): Once SVG USE element doesn't modify DOM tree, we
-  // should get rid of this update layout call.
-  // See http://crbug.com/566281
-  // See "svg/text/textpath-reference-crash.html"
-  GetFrame()->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
-
   Position base_position(base_node, base_offset);
   Position extent_position(extent_node, extent_offset);
   Range* new_range = Range::Create(base_node->GetDocument());
diff --git a/third_party/WebKit/Source/core/frame/BUILD.gn b/third_party/WebKit/Source/core/frame/BUILD.gn
index e6f88fcc..de8783f 100644
--- a/third_party/WebKit/Source/core/frame/BUILD.gn
+++ b/third_party/WebKit/Source/core/frame/BUILD.gn
@@ -117,6 +117,7 @@
     "UseCounter.h",
     "VisualViewport.cpp",
     "VisualViewport.h",
+    "WebLocalFrameBase.cpp",
     "WebLocalFrameBase.h",
     "csp/CSPDirective.h",
     "csp/CSPDirectiveList.cpp",
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameBase.cpp b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.cpp
new file mode 100644
index 0000000..3707f52
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.cpp
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/frame/WebLocalFrameBase.h"
+
+#include "core/page/ChromeClient.h"
+#include "core/page/Page.h"
+
+namespace blink {
+
+WebLocalFrameBase* WebLocalFrameBase::FromFrame(LocalFrame* frame) {
+  if (frame && frame->GetPage()) {
+    return frame->GetPage()->GetChromeClient().GetWebLocalFrameBase(frame);
+  }
+  return nullptr;
+}
+
+WebLocalFrameBase* WebLocalFrameBase::FromFrame(LocalFrame& frame) {
+  return WebLocalFrameBase::FromFrame(&frame);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h
index 4c41212..3e05917 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h
@@ -5,10 +5,13 @@
 #ifndef WebLocalFrameBase_h
 #define WebLocalFrameBase_h
 
+#include "core/CoreExport.h"
 #include "public/web/WebLocalFrame.h"
 
 namespace blink {
 
+class LocalFrame;
+
 // WebLocalFrameBase is a temporary class the provides a layer of abstraction
 // for WebLocalFrameImpl. Mehtods that are declared public in WebLocalFrameImpl
 // that are not overrides from WebLocalFrame will be declared pure virtual in
@@ -18,6 +21,10 @@
 // modules.
 // TODO(slangley): Remove this class once WebLocalFrameImpl is in core/.
 class WebLocalFrameBase : public WebLocalFrame {
+ public:
+  CORE_EXPORT static WebLocalFrameBase* FromFrame(LocalFrame*);
+  CORE_EXPORT static WebLocalFrameBase* FromFrame(LocalFrame&);
+
  protected:
   explicit WebLocalFrameBase(WebTreeScopeType scope) : WebLocalFrame(scope) {}
 };
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.cpp b/third_party/WebKit/Source/core/layout/LayoutText.cpp
index c7b873fe..6e007fd6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutText.cpp
@@ -343,21 +343,18 @@
 
 static FloatRect LocalQuadForTextBox(InlineTextBox* box,
                                      unsigned start,
-                                     unsigned end,
-                                     bool use_selection_height) {
+                                     unsigned end) {
   unsigned real_end = std::min(box->end() + 1, end);
   LayoutRect r = box->LocalSelectionRect(start, real_end);
   if (r.Height()) {
-    if (!use_selection_height) {
-      // Change the height and y position (or width and x for vertical text)
-      // because selectionRect uses selection-specific values.
-      if (box->IsHorizontal()) {
-        r.SetHeight(box->Height());
-        r.SetY(box->Y());
-      } else {
-        r.SetWidth(box->Width());
-        r.SetX(box->X());
-      }
+    // Change the height and y position (or width and x for vertical text)
+    // because selectionRect uses selection-specific values.
+    if (box->IsHorizontal()) {
+      r.SetHeight(box->Height());
+      r.SetY(box->Y());
+    } else {
+      r.SetWidth(box->Width());
+      r.SetX(box->X());
     }
     return FloatRect(r);
   }
@@ -366,8 +363,7 @@
 
 void LayoutText::AbsoluteRectsForRange(Vector<IntRect>& rects,
                                        unsigned start,
-                                       unsigned end,
-                                       bool use_selection_height) const {
+                                       unsigned end) const {
   // Work around signed/unsigned issues. This function takes unsigneds, and is
   // often passed UINT_MAX to mean "all the way to the end". InlineTextBox
   // coordinates are unsigneds, so changing this function to take ints causes
@@ -388,16 +384,6 @@
     // past it
     if (start <= box->Start() && box->end() < end) {
       FloatRect r(box->FrameRect());
-      if (use_selection_height) {
-        LayoutRect selection_rect = box->LocalSelectionRect(start, end);
-        if (box->IsHorizontal()) {
-          r.SetHeight(selection_rect.Height().ToFloat());
-          r.SetY(selection_rect.Y().ToFloat());
-        } else {
-          r.SetWidth(selection_rect.Width().ToFloat());
-          r.SetX(selection_rect.X().ToFloat());
-        }
-      }
       if (!has_checked_box_in_range) {
         has_checked_box_in_range = true;
         rects.clear();
@@ -405,8 +391,7 @@
       rects.push_back(LocalToAbsoluteQuad(r).EnclosingBoundingBox());
     } else if ((box->Start() <= start && start <= box->end()) ||
                (box->Start() < end && end <= box->end())) {
-      FloatRect rect =
-          LocalQuadForTextBox(box, start, end, use_selection_height);
+      FloatRect rect = LocalQuadForTextBox(box, start, end);
       if (!rect.IsZero()) {
         if (!has_checked_box_in_range) {
           has_checked_box_in_range = true;
@@ -417,8 +402,7 @@
     } else if (!has_checked_box_in_range) {
       // FIXME: This code is wrong. It's converting local to absolute twice.
       // http://webkit.org/b/65722
-      FloatRect rect =
-          LocalQuadForTextBox(box, start, end, use_selection_height);
+      FloatRect rect = LocalQuadForTextBox(box, start, end);
       if (!rect.IsZero())
         rects.push_back(LocalToAbsoluteQuad(rect).EnclosingBoundingBox());
     }
@@ -523,7 +507,7 @@
       quads.push_back(LocalToAbsoluteQuad(FloatRect(r)));
     } else if ((box->Start() <= start && start <= box->end()) ||
                (box->Start() < end && end <= box->end())) {
-      FloatRect rect = LocalQuadForTextBox(box, start, end, false);
+      FloatRect rect = LocalQuadForTextBox(box, start, end);
       if (!rect.IsZero()) {
         if (!has_checked_box_in_range) {
           has_checked_box_in_range = true;
@@ -534,7 +518,7 @@
     } else if (!has_checked_box_in_range) {
       // consider when the offset of range is area of leading or trailing
       // whitespace
-      FloatRect rect = LocalQuadForTextBox(box, start, end, false);
+      FloatRect rect = LocalQuadForTextBox(box, start, end);
       if (!rect.IsZero())
         quads.push_back(LocalToAbsoluteQuad(rect).EnclosingBoundingBox());
     }
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.h b/third_party/WebKit/Source/core/layout/LayoutText.h
index c675d282..6462cadf 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.h
+++ b/third_party/WebKit/Source/core/layout/LayoutText.h
@@ -101,8 +101,7 @@
                      const LayoutPoint& accumulated_offset) const final;
   void AbsoluteRectsForRange(Vector<IntRect>&,
                              unsigned start_offset = 0,
-                             unsigned end_offset = INT_MAX,
-                             bool use_selection_height = false) const;
+                             unsigned end_offset = INT_MAX) const;
 
   void AbsoluteQuads(Vector<FloatQuad>&,
                      MapCoordinatesFlags mode = 0) const final;
diff --git a/third_party/WebKit/Source/core/page/ChromeClient.h b/third_party/WebKit/Source/core/page/ChromeClient.h
index 343eb97..48e803a2 100644
--- a/third_party/WebKit/Source/core/page/ChromeClient.h
+++ b/third_party/WebKit/Source/core/page/ChromeClient.h
@@ -77,6 +77,7 @@
 class WebImage;
 class WebLayer;
 class WebLayerTreeView;
+class WebLocalFrameBase;
 
 struct CompositedSelection;
 struct DateTimeChooserParameters;
@@ -352,6 +353,10 @@
 
   virtual WebLayerTreeView* GetWebLayerTreeView(LocalFrame*) { return nullptr; }
 
+  virtual WebLocalFrameBase* GetWebLocalFrameBase(LocalFrame*) {
+    return nullptr;
+  }
+
   DECLARE_TRACE();
 
  protected:
diff --git a/third_party/WebKit/Source/core/style/BUILD.gn b/third_party/WebKit/Source/core/style/BUILD.gn
index 2c449ce..1458f9a 100644
--- a/third_party/WebKit/Source/core/style/BUILD.gn
+++ b/third_party/WebKit/Source/core/style/BUILD.gn
@@ -103,8 +103,6 @@
     "StyleSelfAlignmentData.h",
     "StyleTransformData.cpp",
     "StyleTransformData.h",
-    "StyleVisualData.cpp",
-    "StyleVisualData.h",
     "StyleWillChangeData.cpp",
     "StyleWillChangeData.h",
     "TextSizeAdjust.h",
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index f4ddbc2..522a8a5 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -115,7 +115,6 @@
 ALWAYS_INLINE ComputedStyle::ComputedStyle()
     : ComputedStyleBase(), RefCounted<ComputedStyle>() {
   box_data_.Init();
-  visual_data_.Init();
   rare_non_inherited_data_.Init();
   rare_non_inherited_data_.Access()->deprecated_flexible_box_.Init();
   rare_non_inherited_data_.Access()->flexible_box_.Init();
@@ -136,7 +135,6 @@
     : ComputedStyleBase(o),
       RefCounted<ComputedStyle>(),
       box_data_(o.box_data_),
-      visual_data_(o.visual_data_),
       rare_non_inherited_data_(o.rare_non_inherited_data_),
       rare_inherited_data_(o.rare_inherited_data_),
       inherited_data_(o.inherited_data_),
@@ -330,7 +328,6 @@
 void ComputedStyle::CopyNonInheritedFromCached(const ComputedStyle& other) {
   ComputedStyleBase::CopyNonInheritedFromCached(other);
   box_data_ = other.box_data_;
-  visual_data_ = other.visual_data_;
   rare_non_inherited_data_ = other.rare_non_inherited_data_;
 
   // The flags are copied one-by-one because they contain
@@ -471,7 +468,6 @@
   // compare everything except the pseudoStyle pointer
   return ComputedStyleBase::NonInheritedEqual(other) &&
          box_data_ == other.box_data_ &&
-         visual_data_ == other.visual_data_ &&
          rare_non_inherited_data_ == other.rare_non_inherited_data_ &&
          svg_style_->NonInheritedEqual(*other.svg_style_);
 }
@@ -1057,7 +1053,8 @@
         inherited_data_->visited_link_color_ !=
             other.inherited_data_->visited_link_color_ ||
         HasSimpleUnderlineInternal() != other.HasSimpleUnderlineInternal() ||
-        visual_data_->text_decoration != other.visual_data_->text_decoration) {
+        visual_data_->text_decoration_ !=
+            other.visual_data_->text_decoration_) {
       diff.SetTextDecorationOrColorChanged();
     } else {
       if (rare_non_inherited_data_.Get() !=
@@ -1102,11 +1099,11 @@
     }
   }
 
-  bool has_clip = HasOutOfFlowPosition() && !visual_data_->has_auto_clip;
+  bool has_clip = HasOutOfFlowPosition() && !visual_data_->has_auto_clip_;
   bool other_has_clip =
-      other.HasOutOfFlowPosition() && !other.visual_data_->has_auto_clip;
+      other.HasOutOfFlowPosition() && !other.visual_data_->has_auto_clip_;
   if (has_clip != other_has_clip ||
-      (has_clip && visual_data_->clip != other.visual_data_->clip))
+      (has_clip && visual_data_->clip_ != other.visual_data_->clip_))
     diff.SetCSSClipChanged();
 }
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 9034275..d0b5313f 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -53,7 +53,6 @@
 #include "core/style/StyleReflection.h"
 #include "core/style/StyleSelfAlignmentData.h"
 #include "core/style/StyleTransformData.h"
-#include "core/style/StyleVisualData.h"
 #include "core/style/StyleWillChangeData.h"
 #include "core/style/TransformOrigin.h"
 #include "platform/Length.h"
@@ -184,7 +183,6 @@
  protected:
   // non-inherited attributes
   DataRef<StyleBoxData> box_data_;
-  DataRef<StyleVisualData> visual_data_;
   DataRef<StyleRareNonInheritedData> rare_non_inherited_data_;
 
   // inherited attributes
@@ -576,15 +574,15 @@
 
   // clip
   static LengthBox InitialClip() { return LengthBox(); }
-  const LengthBox& Clip() const { return visual_data_->clip; }
+  const LengthBox& Clip() const { return visual_data_->clip_; }
   void SetClip(const LengthBox& box) {
-    SET_VAR(visual_data_, has_auto_clip, false);
-    SET_VAR(visual_data_, clip, box);
+    SET_VAR(visual_data_, has_auto_clip_, false);
+    SET_VAR(visual_data_, clip_, box);
   }
-  bool HasAutoClip() const { return visual_data_->has_auto_clip; }
+  bool HasAutoClip() const { return visual_data_->has_auto_clip_; }
   void SetHasAutoClip() {
-    SET_VAR(visual_data_, has_auto_clip, true);
-    SET_VAR(visual_data_, clip, ComputedStyle::InitialClip());
+    SET_VAR(visual_data_, has_auto_clip_, true);
+    SET_VAR(visual_data_, clip_, ComputedStyle::InitialClip());
   }
 
   // Column properties.
@@ -1454,10 +1452,10 @@
   // text-decoration-line
   static TextDecoration InitialTextDecoration() { return kTextDecorationNone; }
   TextDecoration GetTextDecoration() const {
-    return static_cast<TextDecoration>(visual_data_->text_decoration);
+    return static_cast<TextDecoration>(visual_data_->text_decoration_);
   }
   void SetTextDecoration(TextDecoration v) {
-    SET_VAR(visual_data_, text_decoration, v);
+    SET_VAR(visual_data_, text_decoration_, v);
   }
 
   // text-decoration-color
@@ -2971,10 +2969,10 @@
   }
 
   // Clip utility functions.
-  const Length& ClipLeft() const { return visual_data_->clip.Left(); }
-  const Length& ClipRight() const { return visual_data_->clip.Right(); }
-  const Length& ClipTop() const { return visual_data_->clip.Top(); }
-  const Length& ClipBottom() const { return visual_data_->clip.Bottom(); }
+  const Length& ClipLeft() const { return visual_data_->clip_.Left(); }
+  const Length& ClipRight() const { return visual_data_->clip_.Right(); }
+  const Length& ClipTop() const { return visual_data_->clip_.Top(); }
+  const Length& ClipBottom() const { return visual_data_->clip_.Bottom(); }
 
   // Offset utility functions.
   // Accessors for positioned object edges that take into account writing mode.
diff --git a/third_party/WebKit/Source/core/style/StyleVisualData.cpp b/third_party/WebKit/Source/core/style/StyleVisualData.cpp
deleted file mode 100644
index 89541881..0000000
--- a/third_party/WebKit/Source/core/style/StyleVisualData.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#include "core/style/StyleVisualData.h"
-
-#include "core/style/ComputedStyle.h"
-
-namespace blink {
-
-StyleVisualData::StyleVisualData()
-    : has_auto_clip(true),
-      text_decoration(ComputedStyle::InitialTextDecoration()),
-      zoom_(ComputedStyle::InitialZoom()) {}
-
-StyleVisualData::~StyleVisualData() {}
-
-StyleVisualData::StyleVisualData(const StyleVisualData& o)
-    : RefCounted<StyleVisualData>(),
-      clip(o.clip),
-      has_auto_clip(o.has_auto_clip),
-      text_decoration(o.text_decoration),
-      zoom_(o.zoom_) {}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/style/StyleVisualData.h b/third_party/WebKit/Source/core/style/StyleVisualData.h
deleted file mode 100644
index 39b6ea6..0000000
--- a/third_party/WebKit/Source/core/style/StyleVisualData.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- *           (C) 2000 Antti Koivisto (koivisto@kde.org)
- *           (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef StyleVisualData_h
-#define StyleVisualData_h
-
-#include "core/CoreExport.h"
-#include "core/style/ComputedStyleConstants.h"
-#include "platform/LengthBox.h"
-#include "platform/wtf/PassRefPtr.h"
-#include "platform/wtf/RefCounted.h"
-
-namespace blink {
-
-// TODO(sashab): Move this into a private class on ComputedStyle, and remove
-// all methods on it, merging them into copy/creation methods on ComputedStyle
-// instead. Keep the allocation logic, only allocating a new object if needed.
-class CORE_EXPORT StyleVisualData : public RefCounted<StyleVisualData> {
- public:
-  static PassRefPtr<StyleVisualData> Create() {
-    return AdoptRef(new StyleVisualData);
-  }
-  PassRefPtr<StyleVisualData> Copy() const {
-    return AdoptRef(new StyleVisualData(*this));
-  }
-  ~StyleVisualData();
-
-  bool operator==(const StyleVisualData& o) const {
-    return clip == o.clip && has_auto_clip == o.has_auto_clip &&
-           text_decoration == o.text_decoration && zoom_ == o.zoom_;
-  }
-  bool operator!=(const StyleVisualData& o) const { return !(*this == o); }
-
-  LengthBox clip;
-  bool has_auto_clip : 1;
-  unsigned text_decoration : kTextDecorationBits;  // Text decorations defined
-                                                   // *only* by this element.
-
-  float zoom_;
-
- private:
-  StyleVisualData();
-  StyleVisualData(const StyleVisualData&);
-};
-
-}  // namespace blink
-
-#endif  // StyleVisualData_h
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
index 4d2c6f1..2c24ab8 100644
--- a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
+++ b/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
@@ -4,11 +4,13 @@
 
 #include "core/workers/MainThreadWorklet.h"
 
+#include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "bindings/core/v8/ScriptSourceCode.h"
 #include "bindings/core/v8/V8BindingForCore.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/TaskRunnerHelper.h"
 #include "core/frame/LocalFrame.h"
 #include "core/workers/WorkletGlobalScopeProxy.h"
 #include "core/workers/WorkletPendingTasks.h"
@@ -18,7 +20,8 @@
 
 MainThreadWorklet::MainThreadWorklet(LocalFrame* frame) : Worklet(frame) {}
 
-// Implementation of the "addModule(moduleURL, options)" algorithm:
+// Implementation of the first half of the "addModule(moduleURL, options)"
+// algorithm:
 // https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule
 ScriptPromise MainThreadWorklet::addModule(ScriptState* script_state,
                                            const String& module_url) {
@@ -50,7 +53,24 @@
 
   // Step 5: "Return promise, and then continue running this algorithm in
   // parallel."
-  // TODO(nhiroki): Make the following sequence async.
+  // |kUnspecedLoading| is used here because this is a part of script module
+  // loading.
+  TaskRunnerHelper::Get(TaskType::kUnspecedLoading, script_state)
+      ->PostTask(BLINK_FROM_HERE,
+                 WTF::Bind(&MainThreadWorklet::FetchAndInvokeScript,
+                           WrapPersistent(this), module_url_record,
+                           WrapPersistent(resolver)));
+  return promise;
+}
+
+// Implementation of the second half of the "addModule(moduleURL, options)"
+// algorithm:
+// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule
+void MainThreadWorklet::FetchAndInvokeScript(const KURL& module_url_record,
+                                             ScriptPromiseResolver* resolver) {
+  DCHECK(IsMainThread());
+  if (!GetExecutionContext())
+    return;
 
   // Step 6: "Let credentialOptions be the credentials member of options."
   // TODO(nhiroki): Implement credentialOptions (https://crbug.com/710837).
@@ -93,7 +113,6 @@
   // TODO(nhiroki): Queue a task instead of executing this here.
   GetWorkletGlobalScopeProxy()->FetchAndInvokeScript(module_url_record,
                                                      pending_tasks);
-  return promise;
 }
 
 void MainThreadWorklet::ContextDestroyed(ExecutionContext* execution_context) {
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorklet.h b/third_party/WebKit/Source/core/workers/MainThreadWorklet.h
index de5853b..e6b5c85 100644
--- a/third_party/WebKit/Source/core/workers/MainThreadWorklet.h
+++ b/third_party/WebKit/Source/core/workers/MainThreadWorklet.h
@@ -7,13 +7,14 @@
 
 #include "core/workers/Worklet.h"
 
-#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "bindings/core/v8/ScriptPromise.h"
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
 class LocalFrame;
+class ScriptPromiseResolver;
 
 // A MainThreadWorklet is a worklet that runs only on the main thread.
 // TODO(nhiroki): This is a temporary class to support module loading for main
@@ -36,6 +37,10 @@
 
  protected:
   explicit MainThreadWorklet(LocalFrame*);
+
+ private:
+  void FetchAndInvokeScript(const KURL& module_url_record,
+                            ScriptPromiseResolver*);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index 6ec80ab..34e37bb1 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -950,6 +950,10 @@
   return web_frame->LocalRoot()->FrameWidget()->GetLayerTreeView();
 }
 
+WebLocalFrameBase* ChromeClientImpl::GetWebLocalFrameBase(LocalFrame* frame) {
+  return WebLocalFrameImpl::FromFrame(frame);
+}
+
 void ChromeClientImpl::SetEventListenerProperties(
     LocalFrame* frame,
     WebEventListenerClass event_class,
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.h b/third_party/WebKit/Source/web/ChromeClientImpl.h
index 0b2722a..cf0cecf 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.h
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.h
@@ -235,6 +235,8 @@
 
   WebLayerTreeView* GetWebLayerTreeView(LocalFrame*) override;
 
+  WebLocalFrameBase* GetWebLocalFrameBase(LocalFrame*) override;
+
  private:
   explicit ChromeClientImpl(WebViewBase*);
 
diff --git a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp b/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
index 6321465..c1e00ad 100644
--- a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
@@ -107,7 +107,7 @@
 static String SelectMisspellingAsync(LocalFrame* selected_frame,
                                      String& description) {
   VisibleSelection selection =
-      selected_frame->Selection().ComputeVisibleSelectionInDOMTreeDeprecated();
+      selected_frame->Selection().ComputeVisibleSelectionInDOMTree();
   if (selection.IsNone())
     return String();
 
diff --git a/third_party/WebKit/Source/web/WebDocument.cpp b/third_party/WebKit/Source/web/WebDocument.cpp
index cd6040c..110117f 100644
--- a/third_party/WebKit/Source/web/WebDocument.cpp
+++ b/third_party/WebKit/Source/web/WebDocument.cpp
@@ -41,6 +41,7 @@
 #include "core/dom/Element.h"
 #include "core/dom/StyleEngine.h"
 #include "core/events/Event.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "core/html/HTMLAllCollection.h"
 #include "core/html/HTMLBodyElement.h"
 #include "core/html/HTMLCollection.h"
@@ -65,7 +66,6 @@
 #include "public/web/WebElementCollection.h"
 #include "public/web/WebFormElement.h"
 #include "v8/include/v8.h"
-#include "web/WebLocalFrameImpl.h"
 
 namespace blink {
 
@@ -106,7 +106,7 @@
 }
 
 WebLocalFrame* WebDocument::GetFrame() const {
-  return WebLocalFrameImpl::FromFrame(ConstUnwrap<Document>()->GetFrame());
+  return WebLocalFrameBase::FromFrame(ConstUnwrap<Document>()->GetFrame());
 }
 
 bool WebDocument::IsHTMLDocument() const {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
index 067d358..98ab9ee 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer.py
@@ -36,7 +36,6 @@
 
 
 class BaselineOptimizer(object):
-    ROOT_LAYOUT_TESTS_DIRECTORY = 'LayoutTests'
 
     def __init__(self, host, port, port_names):
         self._filesystem = host.filesystem
@@ -45,8 +44,10 @@
         for port_name in port_names:
             self._ports[port_name] = host.port_factory.get(port_name)
 
-        self._webkit_base = port.webkit_base()
         self._layout_tests_dir = port.layout_tests_dir()
+        self._parent_of_tests = self._filesystem.dirname(self._layout_tests_dir)
+        self._layout_tests_dir_name = self._filesystem.relpath(
+            self._layout_tests_dir, self._parent_of_tests)
 
         # Only used by unit tests.
         self.new_results_by_directory = []
@@ -158,10 +159,10 @@
             _log.debug('    (Nothing to add)')
 
     def _platform(self, filename):
-        platform_dir = self.ROOT_LAYOUT_TESTS_DIRECTORY + self._filesystem.sep + 'platform' + self._filesystem.sep
+        platform_dir = self._layout_tests_dir_name + self._filesystem.sep + 'platform' + self._filesystem.sep
         if filename.startswith(platform_dir):
             return filename.replace(platform_dir, '').split(self._filesystem.sep)[0]
-        platform_dir = self._filesystem.join(self._webkit_base, platform_dir)
+        platform_dir = self._filesystem.join(self._parent_of_tests, platform_dir)
         if filename.startswith(platform_dir):
             return filename.replace(platform_dir, '').split(self._filesystem.sep)[0]
         return '(generic)'
@@ -190,8 +191,8 @@
     def _baseline_root(self, baseline_name):
         virtual_suite = self._virtual_suite(baseline_name)
         if virtual_suite:
-            return self._filesystem.join(self.ROOT_LAYOUT_TESTS_DIRECTORY, virtual_suite.name)
-        return self.ROOT_LAYOUT_TESTS_DIRECTORY
+            return self._filesystem.join(self._layout_tests_dir_name, virtual_suite.name)
+        return self._layout_tests_dir_name
 
     def _baseline_search_path(self, port, baseline_name):
         virtual_suite = self._virtual_suite(baseline_name)
@@ -209,7 +210,7 @@
         """Returns a list of paths to check for baselines in order."""
         baseline_search_path = self._baseline_search_path(port, baseline_name)
         baseline_root = self._baseline_root(baseline_name)
-        relative_paths = [self._filesystem.relpath(path, self._webkit_base) for path in baseline_search_path]
+        relative_paths = [self._filesystem.relpath(path, self._parent_of_tests) for path in baseline_search_path]
         return relative_paths + [baseline_root]
 
     def _join_directory(self, directory, baseline_name):
@@ -226,7 +227,7 @@
             baseline_name_without_virtual = baseline_name[len(virtual_suite.name) + 1:]
         else:
             baseline_name_without_virtual = baseline_name
-        return self._filesystem.join(self._webkit_base, directory, baseline_name_without_virtual)
+        return self._filesystem.join(self._parent_of_tests, directory, baseline_name_without_virtual)
 
     def _results_by_port_name(self, results_by_directory, baseline_name):
         results_by_port_name = {}
@@ -241,7 +242,7 @@
     def _directories_immediately_preceding_root(self, baseline_name):
         directories = set()
         for port in self._ports.values():
-            directory = self._filesystem.relpath(self._baseline_search_path(port, baseline_name)[-1], self._webkit_base)
+            directory = self._filesystem.relpath(self._baseline_search_path(port, baseline_name)[-1], self._parent_of_tests)
             directories.add(directory)
         return directories
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer_unittest.py
index 7514e04..f7f142ba 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baseline_optimizer_unittest.py
@@ -38,20 +38,20 @@
     def _assert_optimization(self, results_by_directory, directory_to_new_results, baseline_dirname='', host=None):
         host = host or MockHost()
         fs = host.filesystem
-        webkit_base = WebKitFinder(fs).webkit_base()
+        layout_tests_dir = WebKitFinder(fs).layout_tests_dir()
         baseline_name = 'mock-baseline-expected.txt'
         fs.write_text_file(
-            fs.join(webkit_base, 'LayoutTests', 'VirtualTestSuites'),
+            fs.join(layout_tests_dir, 'VirtualTestSuites'),
             '[{"prefix": "gpu", "base": "fast/canvas", "args": ["--foo"]}]')
 
         for dirname, contents in results_by_directory.items():
-            fs.write_binary_file(fs.join(webkit_base, 'LayoutTests', dirname, baseline_name), contents)
+            fs.write_binary_file(fs.join(layout_tests_dir, dirname, baseline_name), contents)
 
         baseline_optimizer = BaselineOptimizer(host, host.port_factory.get(), host.port_factory.all_port_names())
         self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, baseline_name)))
 
         for dirname, contents in directory_to_new_results.items():
-            path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
+            path = fs.join(layout_tests_dir, dirname, baseline_name)
             if contents is not None:
                 self.assertEqual(fs.read_binary_file(path), contents)
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
index d87b6474..e5863a0 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_manifest.py
@@ -117,7 +117,7 @@
 
         cmd = ['python', manifest_exec_path, '--work', '--tests-root', dest_path]
         _log.debug('Running command: %s', ' '.join(cmd))
-        proc = executive.popen(cmd, stdout=executive.PIPE, stderr=executive.PIPE, stdin=executive.PIPE, cwd=finder.webkit_base())
+        proc = executive.popen(cmd, stdout=executive.PIPE, stderr=executive.PIPE, stdin=executive.PIPE)
         out, err = proc.communicate('')
         if proc.returncode:
             _log.info('# ret> %d', proc.returncode)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b796bbcd..6968841 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16980,9 +16980,10 @@
 </enum>
 
 <enum name="GeolocationSettingsDialogBackOff" type="int">
-  <int value="0" label="One week"/>
-  <int value="1" label="One month"/>
-  <int value="2" label="Three months"/>
+  <int value="0" label="No backoff"/>
+  <int value="1" label="One week"/>
+  <int value="2" label="One month"/>
+  <int value="3" label="Three months"/>
 </enum>
 
 <enum name="GeopositionErrorCode" type="int">
@@ -21398,6 +21399,7 @@
   <int value="-864205629" label="enable-offline-load-stale-cache"/>
   <int value="-861678473" label="disable-offer-upload-credit-cards"/>
   <int value="-861343291" label="ChromeHome:disabled"/>
+  <int value="-860534647" label="SiteDetails:enabled"/>
   <int value="-856915246" label="enable-new-audio-rendering-mixing-strategy"/>
   <int value="-856016114" label="NTPOfflinePages:disabled"/>
   <int value="-855130893" label="enable-touch-calibration-setting"/>
@@ -21738,6 +21740,7 @@
   <int value="546710806" label="disable-easy-signin"/>
   <int value="550378029" label="reset-app-list-install-state"/>
   <int value="550387510" label="NTPAssetDownloadSuggestions:disabled"/>
+  <int value="558873715" label="SiteDetails:disabled"/>
   <int value="562979188" label="DesktopIOSPromotion:enabled"/>
   <int value="567368307" label="enable-experimental-canvas-features"/>
   <int value="575394365" label="AndroidPaymentApps:disabled"/>
diff --git a/ui/arc/notification/arc_custom_notification_view.cc b/ui/arc/notification/arc_custom_notification_view.cc
index 7c72f3a..d28a6dd7 100644
--- a/ui/arc/notification/arc_custom_notification_view.cc
+++ b/ui/arc/notification/arc_custom_notification_view.cc
@@ -197,12 +197,6 @@
     owner_->UpdateControlButtonsVisibility();
   }
 
-  bool IsPinned() const override {
-    if (!owner_->item_)
-      return false;
-    return owner_->item_->pinned();
-  }
-
   void UpdateControlButtonsVisibility() override {
     owner_->UpdateControlButtonsVisibility();
   }
diff --git a/ui/message_center/views/custom_notification_content_view_delegate.h b/ui/message_center/views/custom_notification_content_view_delegate.h
index c61e8b9b..3d84d00 100644
--- a/ui/message_center/views/custom_notification_content_view_delegate.h
+++ b/ui/message_center/views/custom_notification_content_view_delegate.h
@@ -21,7 +21,6 @@
  public:
   virtual bool IsCloseButtonFocused() const = 0;
   virtual void RequestFocusOnCloseButton() = 0;
-  virtual bool IsPinned() const = 0;
   virtual void UpdateControlButtonsVisibility() = 0;
 };
 
diff --git a/ui/message_center/views/custom_notification_view.cc b/ui/message_center/views/custom_notification_view.cc
index 324db5d..c20104b 100644
--- a/ui/message_center/views/custom_notification_view.cc
+++ b/ui/message_center/views/custom_notification_view.cc
@@ -75,12 +75,6 @@
     contents_view_delegate_->RequestFocusOnCloseButton();
 }
 
-bool CustomNotificationView::IsPinned() const {
-  if (!contents_view_delegate_)
-    return false;
-  return contents_view_delegate_->IsPinned();
-}
-
 const char* CustomNotificationView::GetClassName() const {
   return kViewClassName;
 }
diff --git a/ui/message_center/views/custom_notification_view.h b/ui/message_center/views/custom_notification_view.h
index 31b7a0d..6cd7f83a 100644
--- a/ui/message_center/views/custom_notification_view.h
+++ b/ui/message_center/views/custom_notification_view.h
@@ -38,7 +38,6 @@
   void SetDrawBackgroundAsActive(bool active) override;
   bool IsCloseButtonFocused() const override;
   void RequestFocusOnCloseButton() override;
-  bool IsPinned() const override;
   void UpdateControlButtonsVisibility() override;
 
   // Overridden from views::View:
diff --git a/ui/message_center/views/custom_notification_view_unittest.cc b/ui/message_center/views/custom_notification_view_unittest.cc
index 3f1a3670..158e010 100644
--- a/ui/message_center/views/custom_notification_view_unittest.cc
+++ b/ui/message_center/views/custom_notification_view_unittest.cc
@@ -76,7 +76,6 @@
  public:
   bool IsCloseButtonFocused() const override { return false; }
   void RequestFocusOnCloseButton() override {}
-  bool IsPinned() const override { return false; }
   void UpdateControlButtonsVisibility() override {}
 };
 
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc
index defba47..903f8b03 100644
--- a/ui/message_center/views/message_center_view.cc
+++ b/ui/message_center/views/message_center_view.cc
@@ -643,7 +643,7 @@
   if (mode_ == Mode::NOTIFICATIONS) {
     bool no_closable_views = true;
     for (const auto& view : notification_views_) {
-      if (!view.second->IsPinned()) {
+      if (!view.second->pinned()) {
         no_closable_views = false;
         break;
       }
@@ -676,11 +676,11 @@
     if ((*iter)->id() == id) {
       int old_width = view->width();
       int old_height = view->height();
-      bool old_pinned = view->IsPinned();
+      bool old_pinned = view->pinned();
       message_list_view_->UpdateNotification(view, **iter);
       if (view->GetHeightForWidth(old_width) != old_height) {
         Update(true /* animate */);
-      } else if (view->IsPinned() != old_pinned) {
+      } else if (view->pinned() != old_pinned) {
         // Animate flag is false, since the pinned flag transition doesn't need
         // animation.
         Update(false /* animate */);
diff --git a/ui/message_center/views/message_list_view.cc b/ui/message_center/views/message_list_view.cc
index 06cce39..28cb247 100644
--- a/ui/message_center/views/message_list_view.cc
+++ b/ui/message_center/views/message_list_view.cc
@@ -261,7 +261,7 @@
       continue;
     if (gfx::IntersectRects(child->bounds(), visible_scroll_rect).IsEmpty())
       continue;
-    if (child->IsPinned())
+    if (child->pinned())
       continue;
     if (deleting_views_.find(child) != deleting_views_.end() ||
         deleted_when_done_.find(child) != deleted_when_done_.end()) {
@@ -349,15 +349,17 @@
     return;
   }
 
-  int new_height = GetHeightForWidth(child_area.width() + GetInsets().width());
-  SetSize(gfx::Size(child_area.width() + GetInsets().width(), new_height));
-
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableMessageCenterAlwaysScrollUpUponNotificationRemoval))
     AnimateNotificationsBelowTarget();
   else
     AnimateNotifications();
 
+  // Should calculate and set new size after calling AnimateNotifications()
+  // because fixed_height_ may be updated in it.
+  int new_height = GetHeightForWidth(child_area.width() + GetInsets().width());
+  SetSize(gfx::Size(child_area.width() + GetInsets().width(), new_height));
+
   adding_views_.clear();
   deleting_views_.clear();
 
diff --git a/ui/message_center/views/message_list_view_unittest.cc b/ui/message_center/views/message_list_view_unittest.cc
index d6054ef..8d139a8 100644
--- a/ui/message_center/views/message_list_view_unittest.cc
+++ b/ui/message_center/views/message_list_view_unittest.cc
@@ -24,6 +24,7 @@
 namespace message_center {
 
 static const char* kNotificationId1 = "notification id 1";
+static const char* kNotificationId2 = "notification id 2";
 
 namespace {
 
@@ -131,6 +132,8 @@
 
   int& fixed_height() { return message_list_view_->fixed_height_; }
 
+  views::BoundsAnimator& animator() { return message_list_view_->animator_; }
+
   std::vector<int> ComputeRepositionOffsets(const std::vector<int>& heights,
                                             const std::vector<bool>& deleting,
                                             int target_index,
@@ -144,6 +147,11 @@
     return new MockNotificationView(this, notification, this);
   }
 
+  void RunPendingAnimations() {
+    while (animator().IsAnimating())
+      RunPendingMessages();
+  }
+
  private:
   // MockNotificationView::Test override
   void RegisterCall(CallType type) override {}
@@ -375,4 +383,73 @@
   EXPECT_EQ(5 + top + 2, reposition_top());
 }
 
+TEST_F(MessageListViewTest, RemoveNotification) {
+  message_list_view()->SetBounds(0, 0, 800, 600);
+
+  // Create dummy notifications.
+  auto* notification_view = CreateNotificationView(
+      Notification(NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId1),
+                   base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message1"),
+                   gfx::Image(), base::UTF8ToUTF16("display source"), GURL(),
+                   NotifierId(NotifierId::APPLICATION, "extension_id"),
+                   message_center::RichNotificationData(), nullptr));
+
+  message_list_view()->AddNotificationAt(notification_view, 0);
+  EXPECT_EQ(1, message_list_view()->child_count());
+  EXPECT_TRUE(message_list_view()->Contains(notification_view));
+
+  RunPendingAnimations();
+
+  message_list_view()->RemoveNotification(notification_view);
+
+  RunPendingAnimations();
+
+  EXPECT_EQ(0, message_list_view()->child_count());
+}
+
+TEST_F(MessageListViewTest, ClearAllClosableNotifications) {
+  message_list_view()->SetBounds(0, 0, 800, 600);
+
+  // Create dummy notifications.
+  auto* notification_view1 = CreateNotificationView(
+      Notification(NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId1),
+                   base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message1"),
+                   gfx::Image(), base::UTF8ToUTF16("display source"), GURL(),
+                   NotifierId(NotifierId::APPLICATION, "extension_id"),
+                   message_center::RichNotificationData(), nullptr));
+  auto* notification_view2 = CreateNotificationView(
+      Notification(NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId2),
+                   base::UTF8ToUTF16("title 2"), base::UTF8ToUTF16("message2"),
+                   gfx::Image(), base::UTF8ToUTF16("display source"), GURL(),
+                   NotifierId(NotifierId::APPLICATION, "extension_id"),
+                   message_center::RichNotificationData(), nullptr));
+
+  message_list_view()->AddNotificationAt(notification_view1, 0);
+  EXPECT_EQ(1, message_list_view()->child_count());
+  EXPECT_TRUE(notification_view1->visible());
+
+  RunPendingAnimations();
+
+  message_list_view()->AddNotificationAt(notification_view2, 1);
+  EXPECT_EQ(2, message_list_view()->child_count());
+  EXPECT_TRUE(notification_view2->visible());
+
+  RunPendingAnimations();
+
+  message_list_view()->ClearAllClosableNotifications(
+      message_list_view()->bounds());
+
+  RunPendingAnimations();
+
+  // TODO(yhanada): notification_view1 and notification_view2 should be deleted
+  //                here. Uncomment the below test.
+  EXPECT_TRUE(gfx::IntersectRects(notification_view1->bounds(),
+                                  message_list_view()->bounds())
+                  .IsEmpty());
+  EXPECT_TRUE(gfx::IntersectRects(notification_view2->bounds(),
+                                  message_list_view()->bounds())
+                  .IsEmpty());
+  // EXPECT_EQ(0, message_list_view()->child_count());
+}
+
 }  // namespace
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index a1fa32c3..6d5aca2 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -86,6 +86,7 @@
 
 void MessageView::UpdateWithNotification(const Notification& notification) {
   display_source_ = notification.display_source();
+  pinned_ = notification.pinned();
   accessible_name_ = CreateAccessibleName(notification);
   slide_out_controller_.set_enabled(!notification.pinned());
 }
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index c35d5394..5942e777 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -55,7 +55,6 @@
 
   virtual bool IsCloseButtonFocused() const = 0;
   virtual void RequestFocusOnCloseButton() = 0;
-  virtual bool IsPinned() const = 0;
   virtual void UpdateControlButtonsVisibility() = 0;
 
   void OnCloseButtonPressed();
@@ -79,6 +78,7 @@
   std::string notification_id() { return notification_id_; }
   NotifierId notifier_id() { return notifier_id_; }
   const base::string16& display_source() const { return display_source_; }
+  bool pinned() const { return pinned_; }
 
   void set_controller(MessageCenterController* controller) {
     controller_ = controller;
@@ -108,6 +108,7 @@
   base::string16 accessible_name_;
 
   base::string16 display_source_;
+  bool pinned_ = false;
 
   std::unique_ptr<views::Painter> focus_painter_;
 
diff --git a/ui/message_center/views/notification_view.cc b/ui/message_center/views/notification_view.cc
index b12da67..1258273 100644
--- a/ui/message_center/views/notification_view.cc
+++ b/ui/message_center/views/notification_view.cc
@@ -408,10 +408,6 @@
     close_button_->RequestFocus();
 }
 
-bool NotificationView::IsPinned() const {
-  return !close_button_;
-}
-
 void NotificationView::CreateOrUpdateTitleView(
     const Notification& notification) {
   if (notification.title().empty()) {
diff --git a/ui/message_center/views/notification_view.h b/ui/message_center/views/notification_view.h
index 127b5a9..0724bfd2 100644
--- a/ui/message_center/views/notification_view.h
+++ b/ui/message_center/views/notification_view.h
@@ -54,7 +54,6 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
   bool IsCloseButtonFocused() const override;
   void RequestFocusOnCloseButton() override;
-  bool IsPinned() const override;
   void UpdateControlButtonsVisibility() override;
 
  protected:
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index cb53de4..3d050476 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -711,6 +711,8 @@
     "test/slider_test_api.h",
     "test/test_layout_manager.cc",
     "test/test_layout_manager.h",
+    "test/test_layout_provider.cc",
+    "test/test_layout_provider.h",
     "test/test_platform_native_widget.h",
     "test/test_views.cc",
     "test/test_views.h",
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 2a15779b..c97fcf3e 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -533,7 +533,9 @@
   if (footnote_container_)
     size.Enlarge(0, footnote_container_->GetHeightForWidth(size.width()));
 
-  if (GetWidget()->widget_delegate()->AsDialogDelegate())
+  DialogDelegate* dialog_delegate =
+      GetWidget()->widget_delegate()->AsDialogDelegate();
+  if (dialog_delegate && dialog_delegate->ShouldSnapFrameWidth())
     size.set_width(LayoutProvider::Get()->GetSnappedDialogWidth(size.width()));
 
   return size;
diff --git a/ui/views/bubble/bubble_frame_view_unittest.cc b/ui/views/bubble/bubble_frame_view_unittest.cc
index 705e1774..bcd120b6 100644
--- a/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -14,6 +14,7 @@
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_dialog_delegate.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/test/test_layout_provider.h"
 #include "ui/views/test/test_views.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget.h"
@@ -502,44 +503,32 @@
 
   using BubbleDialogDelegateView::SetAnchorView;
 
+  void set_override_snap(bool value) { override_snap_ = value; }
+
   // BubbleDialogDelegateView:
   void DeleteDelegate() override {
     // This delegate is owned by the test case itself, so it should not delete
     // itself here.
   }
-
   int GetDialogButtons() const override { return ui::DIALOG_BUTTON_NONE; }
-
+  bool ShouldSnapFrameWidth() const override {
+    return override_snap_.value_or(
+        BubbleDialogDelegateView::ShouldSnapFrameWidth());
+  }
   gfx::Size GetPreferredSize() const override { return gfx::Size(200, 200); }
 
  private:
+  base::Optional<bool> override_snap_;
+
   DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
 };
 
-class TestLayoutProvider : public LayoutProvider {
- public:
-  TestLayoutProvider() : LayoutProvider() {}
-  ~TestLayoutProvider() override {}
-
-  // LayoutProvider:
-  int GetSnappedDialogWidth(int min_width) const override {
-    return snap_to_ ? snap_to_ : min_width;
-  }
-
-  void set_snap_to(int width) { snap_to_ = width; }
-
- private:
-  int snap_to_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestLayoutProvider);
-};
-
 }  // namespace
 
 // This test ensures that if the installed LayoutProvider snaps dialog widths,
 // BubbleFrameView correctly sizes itself to that width.
 TEST_F(BubbleFrameViewTest, WidthSnaps) {
-  TestLayoutProvider provider;
+  test::TestLayoutProvider provider;
   TestBubbleDialogDelegateView delegate;
 
   Widget anchor;
@@ -558,7 +547,7 @@
   w0->CloseNow();
 
   constexpr int kTestWidth = 300;
-  provider.set_snap_to(kTestWidth);
+  provider.SetSnappedDialogWidth(kTestWidth);
 
   // The Widget's snapped width should exactly match the width returned by the
   // LayoutProvider.
@@ -566,6 +555,14 @@
   w1->Show();
   EXPECT_EQ(kTestWidth, w1->GetWindowBoundsInScreen().width());
   w1->CloseNow();
+
+  // If the DialogDelegate asks not to snap, it should not snap.
+  delegate.set_override_snap(false);
+  Widget* w2 = BubbleDialogDelegateView::CreateBubble(&delegate);
+  w2->Show();
+  EXPECT_EQ(delegate.GetPreferredSize().width(),
+            w2->GetWindowBoundsInScreen().width());
+  w2->CloseNow();
 }
 
 }  // namespace views
diff --git a/ui/views/layout/layout_provider.cc b/ui/views/layout/layout_provider.cc
index 3cb034c..5d2f86b 100644
--- a/ui/views/layout/layout_provider.cc
+++ b/ui/views/layout/layout_provider.cc
@@ -56,6 +56,10 @@
 int LayoutProvider::GetDistanceMetric(int metric) const {
   DCHECK_GE(metric, VIEWS_INSETS_MAX);
   switch (metric) {
+    case DistanceMetric::DISTANCE_BUTTON_HORIZONTAL_PADDING:
+      return kButtonHorizontalPadding;
+    case DistanceMetric::DISTANCE_BUTTON_MAX_LINKABLE_WIDTH:
+      return 0;
     case DistanceMetric::DISTANCE_CLOSE_BUTTON_MARGIN:
       return kCloseButtonMargin;
     case DistanceMetric::DISTANCE_RELATED_BUTTON_HORIZONTAL:
@@ -66,8 +70,6 @@
       return kRelatedControlVerticalSpacing;
     case DistanceMetric::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH:
       return kDialogMinimumButtonWidth;
-    case DistanceMetric::DISTANCE_BUTTON_HORIZONTAL_PADDING:
-      return kButtonHorizontalPadding;
   }
   NOTREACHED();
   return 0;
diff --git a/ui/views/layout/layout_provider.h b/ui/views/layout/layout_provider.h
index f6b9640..71c8f7ee 100644
--- a/ui/views/layout/layout_provider.h
+++ b/ui/views/layout/layout_provider.h
@@ -48,6 +48,10 @@
 
   // The default padding to add on each side of a button's label.
   DISTANCE_BUTTON_HORIZONTAL_PADDING = VIEWS_DISTANCE_START,
+  // The maximum width a button can have and still influence the sizes of
+  // other linked buttons.  This allows short buttons to have linked widths
+  // without long buttons making things overly wide.
+  DISTANCE_BUTTON_MAX_LINKABLE_WIDTH,
   // The distance between a dialog's edge and the close button in the upper
   // trailing corner.
   DISTANCE_CLOSE_BUTTON_MARGIN,
diff --git a/ui/views/test/test_layout_provider.cc b/ui/views/test/test_layout_provider.cc
new file mode 100644
index 0000000..a2f3fda
--- /dev/null
+++ b/ui/views/test/test_layout_provider.cc
@@ -0,0 +1,31 @@
+// 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 "ui/views/test/test_layout_provider.h"
+
+namespace views {
+namespace test {
+
+TestLayoutProvider::TestLayoutProvider() {}
+TestLayoutProvider::~TestLayoutProvider() {}
+
+void TestLayoutProvider::SetDistanceMetric(int metric, int value) {
+  distance_metrics_[metric] = value;
+}
+void TestLayoutProvider::SetSnappedDialogWidth(int width) {
+  snapped_dialog_width_ = width;
+}
+
+int TestLayoutProvider::GetDistanceMetric(int metric) const {
+  if (distance_metrics_.count(metric))
+    return distance_metrics_.find(metric)->second;
+  return LayoutProvider::GetDistanceMetric(metric);
+}
+
+int TestLayoutProvider::GetSnappedDialogWidth(int min_width) const {
+  return snapped_dialog_width_ ? snapped_dialog_width_ : min_width;
+}
+
+}  // namespace test
+}  // namespace views
diff --git a/ui/views/test/test_layout_provider.h b/ui/views/test/test_layout_provider.h
new file mode 100644
index 0000000..ede0b65c
--- /dev/null
+++ b/ui/views/test/test_layout_provider.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 UI_VIEWS_TEST_TEST_LAYOUT_PROVIDER_H_
+#define UI_VIEWS_TEST_TEST_LAYOUT_PROVIDER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "ui/views/layout/layout_provider.h"
+
+namespace views {
+namespace test {
+
+// Helper to test LayoutProvider overrides.
+class TestLayoutProvider : public LayoutProvider {
+ public:
+  TestLayoutProvider();
+  ~TestLayoutProvider() override;
+
+  // Override requests for the |metric| DistanceMetric to return |value| rather
+  // than the default.
+  void SetDistanceMetric(int metric, int value);
+
+  // Override the return value of GetSnappedDialogWidth().
+  void SetSnappedDialogWidth(int width);
+
+  // LayoutProvider:
+  int GetDistanceMetric(int metric) const override;
+  int GetSnappedDialogWidth(int min_width) const override;
+
+ private:
+  std::map<int, int> distance_metrics_;
+  int snapped_dialog_width_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLayoutProvider);
+};
+
+}  // namespace test
+}  // namespace views
+
+#endif  // UI_VIEWS_TEST_TEST_LAYOUT_PROVIDER_H_
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 3de458f..775f54d9 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -338,12 +338,13 @@
     return;
 
   gfx::Insets insets = button_row_insets_;
+  LayoutProvider* const layout_provider = LayoutProvider::Get();
   // Support dialogs that clear |button_row_insets_| to do their own layout.
   // They expect GetDialogRelatedControlVerticalSpacing() in this case.
   // TODO(tapted): Remove this under Harmony.
   if (insets.top() == 0) {
-    const int top = LayoutProvider::Get()->GetDistanceMetric(
-        views::DISTANCE_RELATED_CONTROL_VERTICAL);
+    const int top =
+        layout_provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL);
     insets.Set(top, insets.left(), insets.bottom(), insets.right());
   }
 
@@ -355,11 +356,10 @@
   // Button row is [ extra <pad+stretchy> second <pad> third ]. Ensure the <pad>
   // column is zero width if there isn't a button on either side.
   // GetExtraViewSpacing() handles <pad+stretchy>.
-  const int button_spacing =
-      (ok_button_ && cancel_button_)
-          ? LayoutProvider::Get()->GetDistanceMetric(
-                views::DISTANCE_RELATED_BUTTON_HORIZONTAL)
-          : 0;
+  const int button_spacing = (ok_button_ && cancel_button_)
+                                 ? layout_provider->GetDistanceMetric(
+                                       DISTANCE_RELATED_BUTTON_HORIZONTAL)
+                                 : 0;
 
   constexpr int kButtonRowId = 0;
   ColumnSet* column_set = layout->AddColumnSet(kButtonRowId);
@@ -392,18 +392,20 @@
     }
   }
 
-  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
-    // If |views[0]| is non-null, it is a visible |extra_view_| and its column
-    // will be in |link[0]|. Skip that if it is not a button, or if it is a
-    // Checkbox (which extends LabelButton). Otherwise, link everything.
-    bool skip_first_link =
-        views[0] && (!CustomButton::AsCustomButton(views[0]) ||
-                     views[0]->GetClassName() == Checkbox::kViewClassName);
-    if (skip_first_link)
-      column_set->LinkColumnSizes(link[1], link[2], -1);
-    else
-      column_set->LinkColumnSizes(link[0], link[1], link[2], -1);
-  }
+  column_set->set_linked_column_size_limit(
+      layout_provider->GetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH));
+
+  // If |views[0]| is non-null, it is a visible |extra_view_| and its column
+  // will be in |link[0]|. Skip that if it is not a button, or if it is a
+  // Checkbox (which extends LabelButton). Otherwise, link everything.
+  bool skip_first_link =
+      views[0] && (!CustomButton::AsCustomButton(views[0]) ||
+                   views[0]->GetClassName() == Checkbox::kViewClassName);
+  if (skip_first_link)
+    column_set->LinkColumnSizes(link[1], link[2], -1);
+  else
+    column_set->LinkColumnSizes(link[0], link[1], link[2], -1);
+
   layout->AddPaddingRow(kFixed, insets.bottom());
 }
 
diff --git a/ui/views/window/dialog_client_view_unittest.cc b/ui/views/window/dialog_client_view_unittest.cc
index 26ef3e30..877cdb8 100644
--- a/ui/views/window/dialog_client_view_unittest.cc
+++ b/ui/views/window/dialog_client_view_unittest.cc
@@ -4,14 +4,16 @@
 
 #include "ui/views/window/dialog_client_view.h"
 
+#include <map>
+
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "ui/base/test/material_design_controller_test_api.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/test/test_layout_provider.h"
 #include "ui/views/test/test_views.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/widget.h"
@@ -131,7 +133,8 @@
   }
 
   // Set a longer than normal Cancel label so that the minimum button width is
-  // exceeded.
+  // exceeded. The resulting width is around 160 pixels, but depends on system
+  // fonts.
   void SetLongCancelLabel() {
     cancel_label_ = base::ASCIIToUTF16("Cancel Cancel Cancel");
   }
@@ -360,9 +363,8 @@
 
 // Ensure button widths are linked under MD.
 TEST_F(DialogClientViewTest, LinkedWidths) {
-  ui::test::MaterialDesignControllerTestAPI md_test_api(
-      ui::MaterialDesignController::MATERIAL_NORMAL);
-  md_test_api.SetSecondaryUiMaterial(true);
+  test::TestLayoutProvider layout_provider;
+  layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200);
   SetLongCancelLabel();
 
   SetDialogButtons(ui::DIALOG_BUTTON_OK);
@@ -372,6 +374,7 @@
   SetDialogButtons(ui::DIALOG_BUTTON_CANCEL);
   CheckContentsIsSetToPreferredSize();
   const int cancel_button_width = client_view()->cancel_button()->width();
+  EXPECT_LT(cancel_button_width, 200);
 
   // Ensure the single buttons have different preferred widths when alone, and
   // that the Cancel button is bigger (so that it dominates the size).
@@ -383,12 +386,14 @@
   // OK button should now match the bigger, cancel button.
   EXPECT_EQ(cancel_button_width, client_view()->ok_button()->width());
 
-  // But not under non-MD.
-  md_test_api.SetSecondaryUiMaterial(false);
+  // But not when the size of the cancel button exceeds the max linkable width.
+  layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 100);
+  EXPECT_GT(cancel_button_width, 100);
+
   client_view()->UpdateDialogButtons();
   CheckContentsIsSetToPreferredSize();
   EXPECT_EQ(ok_button_only_width, client_view()->ok_button()->width());
-  md_test_api.SetSecondaryUiMaterial(true);
+  layout_provider.SetDistanceMetric(DISTANCE_BUTTON_MAX_LINKABLE_WIDTH, 200);
 
   // The extra view should also match, if it's a matching button type.
   LabelButton* extra_button = new LabelButton(nullptr, base::string16());
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 6af25ee..d354353 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -132,6 +132,10 @@
   button->SetIsDefault(is_default);
 }
 
+bool DialogDelegate::ShouldSnapFrameWidth() const {
+  return true;
+}
+
 int DialogDelegate::GetDialogButtons() const {
   return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
 }
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 6a11e65..9f19c73 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -88,6 +88,10 @@
   // the typical.
   virtual void UpdateButton(LabelButton* button, ui::DialogButton type);
 
+  // Returns true if this dialog should snap the frame width based on the
+  // LayoutProvider's snapping.
+  virtual bool ShouldSnapFrameWidth() const;
+
   // Overridden from ui::DialogModel:
   int GetDialogButtons() const override;
   int GetDefaultDialogButton() const override;
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index a14fe6d..0b6fa0f 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -128,8 +128,8 @@
         -webkit-margin-start: var(--cr-control-spacing);
       }
     </style>
-    <template is="dom-if" if="[[isSpinnerShown_]]">
-      <paper-spinner-lite active>
+    <template is="dom-if" id="spinnerTemplate">
+      <paper-spinner-lite active="[[isSpinnerShown_]]">
       </paper-spinner-lite>
     </template>
     <paper-icon-button id="icon" icon="cr:search" title="[[label]]"
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
index 432c828..0f4f1a5 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
@@ -94,7 +94,10 @@
    * @private
    */
   computeIsSpinnerShown_: function() {
-    return this.spinnerActive && this.showingSearch;
+    var showSpinner = this.spinnerActive && this.showingSearch;
+    if (showSpinner)
+      this.$.spinnerTemplate.if = true;
+    return showSpinner;
   },
 
   /** @private */
diff --git a/ui/webui/resources/js/cr/ui/splitter.js b/ui/webui/resources/js/cr/ui/splitter.js
index b4260e1..b655f5b 100644
--- a/ui/webui/resources/js/cr/ui/splitter.js
+++ b/ui/webui/resources/js/cr/ui/splitter.js
@@ -233,11 +233,15 @@
      */
     handleSplitterDragStart: function() {
       // Use the computed width style as the base so that we can ignore what
-      // box sizing the element has.
+      // box sizing the element has. Add the difference between offset and
+      // client widths to account for any scrollbars.
       var targetElement = this.getResizeTarget_();
       var doc = targetElement.ownerDocument;
       this.startWidth_ =
-          parseFloat(doc.defaultView.getComputedStyle(targetElement).width);
+          parseFloat(doc.defaultView.getComputedStyle(targetElement).width) +
+          targetElement.offsetWidth - targetElement.clientWidth;
+
+      this.classList.add('splitter-active');
     },
 
     /**
@@ -265,6 +269,8 @@
           parseFloat(doc.defaultView.getComputedStyle(targetElement).width);
       if (this.startWidth_ != computedWidth)
         cr.dispatchSimpleEvent(this, 'resize');
+
+      this.classList.remove('splitter-active');
     },
   };