diff --git a/DEPS b/DEPS
index 9d119cc..931f689b 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '4049dffaa8392f6cebab3dd0b9f12fd1eaef4ede',
+  'skia_revision': 'e8f28818a2c0fe967f9fc4cec4bb9dc78af78212',
   # 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': 'c00528b020bff45f3ad997540909dda06629c250',
+  'v8_revision': 'bd232359784b87038ef53685dbae8d623f93852d',
   # 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.
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 66ca252..78f29291 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -631,11 +631,10 @@
 }
 
 bool SystemTray::PerformAction(const ui::Event& event) {
-  // If we're already showing the default view or detailed view in system menu,
-  // hide it; otherwise, show it (and hide any popup that's currently shown).
-  if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT) ||
-      (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED) &&
-       full_system_tray_menu_)) {
+  // If we're already showing a full system tray menu, either default or
+  // detailed menu, hide it; otherwise, show it (and hide any popup that's
+  // currently shown).
+  if (HasSystemBubble() && full_system_tray_menu_) {
     system_bubble_->bubble()->Close();
   } else {
     ShowDefaultView(BUBBLE_CREATE_NEW);
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc
index f67fa93b..9a26ff7 100644
--- a/base/json/string_escape.cc
+++ b/base/json/string_escape.cc
@@ -91,7 +91,9 @@
 
   for (int32_t i = 0; i < length; ++i) {
     uint32_t code_point;
-    if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point)) {
+    if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
+        code_point == static_cast<decltype(code_point)>(CBU_SENTINEL) ||
+        !IsValidCharacter(code_point)) {
       code_point = kReplacementCodePoint;
       did_replacement = true;
     }
diff --git a/base/json/string_escape.h b/base/json/string_escape.h
index b66b7e5..c6a5b33 100644
--- a/base/json/string_escape.h
+++ b/base/json/string_escape.h
@@ -14,11 +14,12 @@
 
 namespace base {
 
-// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units will
-// pass through from the input to the output. Invalid code units will be
-// replaced with the U+FFFD replacement character. This function returns true
-// if no replacement was necessary and false if there was a lossy replacement.
-// On return, |dest| will contain a valid UTF-8 JSON string.
+// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units and
+// characters will pass through from the input to the output. Invalid code
+// units and characters will be replaced with the U+FFFD replacement character.
+// This function returns true if no replacement was necessary and false if
+// there was a lossy replacement. On return, |dest| will contain a valid UTF-8
+// JSON string.
 //
 // Non-printing control characters will be escaped as \uXXXX sequences for
 // readability.
diff --git a/base/json/string_escape_unittest.cc b/base/json/string_escape_unittest.cc
index ae3d82ad..1e962c6 100644
--- a/base/json/string_escape_unittest.cc
+++ b/base/json/string_escape_unittest.cc
@@ -18,14 +18,14 @@
     const char* to_escape;
     const char* escaped;
   } cases[] = {
-    {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
-    {"a\b\f\n\r\t\v\1\\.\"z",
-        "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
-    {"b\x0f\x7f\xf0\xff!",  // \xf0\xff is not a valid UTF-8 unit.
-        "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
-    {"c<>d", "c\\u003C>d"},
-    {"Hello\xe2\x80\xa8world", "Hello\\u2028world"},
-    {"\xe2\x80\xa9purple", "\\u2029purple"},
+      {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+      {"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+      {"b\x0f\x7f\xf0\xff!",  // \xf0\xff is not a valid UTF-8 unit.
+       "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
+      {"c<>d", "c\\u003C>d"},
+      {"Hello\xe2\x80\xa8world", "Hello\\u2028world"},
+      {"\xe2\x80\xa9purple", "\\u2029purple"},
+      {"\xF3\xBF\xBF\xBF", "\xEF\xBF\xBD"},
   };
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java
deleted file mode 100644
index 144d434..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.infobar;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ResourceId;
-import org.chromium.chrome.browser.tab.Tab;
-
-/**
- * An infobar for showing several permission requests which can be allowed or blocked.
- */
-public class GroupedPermissionInfoBar extends PermissionInfoBar {
-    private final String[] mPermissionText;
-    private final int[] mPermissionIcons;
-
-    GroupedPermissionInfoBar(Tab tab, int[] contentSettingsTypes, String message, String linkText,
-            String buttonOk, String buttonCancel, boolean showPersistenceToggle,
-            String[] permissionText, int[] permissionIcons) {
-        super(tab, contentSettingsTypes, 0, null, message, linkText, buttonOk, buttonCancel,
-                showPersistenceToggle);
-        mPermissionText = permissionText;
-        mPermissionIcons = permissionIcons;
-    }
-
-    @Override
-    public void createContent(InfoBarLayout layout) {
-        InfoBarControlLayout control = layout.addControlLayout();
-
-        for (int i = 0; i < mPermissionIcons.length; i++) {
-            control.addIcon(ResourceId.mapToDrawableId(mPermissionIcons[0]),
-                    R.color.light_normal_color, mPermissionText[i], null);
-        }
-
-        // TODO(raymes): Ensure the ALLOW/BLOCK buttons are shown.
-
-        // Call this last to ensure that if a persistence toggle is added, it's added last.
-        super.createContent(layout);
-    }
-
-    @Override
-    @CalledByNative
-    protected boolean isPersistSwitchOn() {
-        return super.isPersistSwitchOn();
-    }
-
-    /**
-     * Create an infobar for a given set of permission requests.
-     *
-     * @param tab                   The tab which owns the infobar.
-     * @param message               Message to display at the top of the infobar.
-     * @param buttonOk              String to display on the 'Allow' button.
-     * @param buttonCancel          String to display on the 'Block' button.
-     * @param permissionIcons       Enumerated ID (from ResourceMapper) of an icon to display next
-     *                              to each permission.
-     * @param permissionText        String to display for each permission request.
-     * @param contentSettingsTypes  The list of ContentSettingsTypes requested by the infobar.
-     * @param showPersistenceToggle Whether or not a toggle to opt-out of persisting a decision
-     *                              should be displayed.
-     */
-    @CalledByNative
-    private static InfoBar create(Tab tab, int[] contentSettingsTypes, String message,
-            String linkText, String buttonOk, String buttonCancel, boolean showPersistenceToggle,
-            String[] permissionText, int[] permissionIcons) {
-        GroupedPermissionInfoBar infobar =
-                new GroupedPermissionInfoBar(tab, contentSettingsTypes, message, linkText, buttonOk,
-                        buttonCancel, showPersistenceToggle, permissionText, permissionIcons);
-        return infobar;
-    }
-}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 29eae3c..9731bcb 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -456,7 +456,6 @@
   "java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBarDelegate.java",
-  "java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java",
   "java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8e8b904..e0fdc42 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -353,6 +353,8 @@
     "download/download_item_model.h",
     "download/download_path_reservation_tracker.cc",
     "download/download_path_reservation_tracker.h",
+    "download/download_permission_request.cc",
+    "download/download_permission_request.h",
     "download/download_prefs.cc",
     "download/download_prefs.h",
     "download/download_query.cc",
@@ -3353,8 +3355,6 @@
       "download/download_danger_prompt.h",
       "download/download_dir_policy_handler.cc",
       "download/download_dir_policy_handler.h",
-      "download/download_permission_request.cc",
-      "download/download_permission_request.h",
       "download/download_shelf.cc",
       "download/download_shelf.h",
       "download/download_shelf_context_menu.cc",
@@ -4229,7 +4229,6 @@
       "../android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBarDelegate.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBarDelegate.java",
-      "../android/java/src/org/chromium/chrome/browser/infobar/GroupedPermissionInfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java",
diff --git a/chrome/browser/download/download_permission_request.cc b/chrome/browser/download/download_permission_request.cc
index e1f30b780..93970ec 100644
--- a/chrome/browser/download/download_permission_request.cc
+++ b/chrome/browser/download/download_permission_request.cc
@@ -4,11 +4,16 @@
 
 #include "chrome/browser/download/download_permission_request.h"
 
-#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/android/android_theme_resources.h"
+#else
+#include "chrome/app/vector_icons/vector_icons.h"
+#endif
+
 DownloadPermissionRequest::DownloadPermissionRequest(
     base::WeakPtr<DownloadRequestLimiter::TabDownloadState> host)
     : host_(host) {
@@ -20,9 +25,19 @@
 DownloadPermissionRequest::~DownloadPermissionRequest() {}
 
 PermissionRequest::IconId DownloadPermissionRequest::GetIconId() const {
+#if defined(OS_ANDROID)
+  return IDR_ANDROID_INFOBAR_MULTIPLE_DOWNLOADS;
+#else
   return kFileDownloadIcon;
+#endif
 }
 
+#if defined(OS_ANDROID)
+base::string16 DownloadPermissionRequest::GetMessageText() const {
+  return l10n_util::GetStringUTF16(IDS_MULTI_DOWNLOAD_WARNING);
+}
+#endif
+
 base::string16 DownloadPermissionRequest::GetMessageTextFragment() const {
   return l10n_util::GetStringUTF16(IDS_MULTI_DOWNLOAD_PERMISSION_FRAGMENT);
 }
diff --git a/chrome/browser/download/download_permission_request.h b/chrome/browser/download/download_permission_request.h
index 66c1cf9..805e9c2 100644
--- a/chrome/browser/download/download_permission_request.h
+++ b/chrome/browser/download/download_permission_request.h
@@ -21,8 +21,11 @@
   ~DownloadPermissionRequest() override;
 
  private:
-  // PermissionBubbleDelegate:
+  // PermissionRequest:
   IconId GetIconId() const override;
+#if defined(OS_ANDROID)
+  base::string16 GetMessageText() const override;
+#endif
   base::string16 GetMessageTextFragment() const override;
   GURL GetOrigin() const override;
   void PermissionGranted() override;
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc
index 3f2a664b..ce379f7 100644
--- a/chrome/browser/download/download_request_limiter.cc
+++ b/chrome/browser/download/download_request_limiter.cc
@@ -8,7 +8,9 @@
 #include "base/stl_util.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/download/download_permission_request.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "components/content_settings/core/browser/content_settings_details.h"
@@ -27,9 +29,6 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/download/download_request_infobar_delegate_android.h"
-#else
-#include "chrome/browser/download/download_permission_request.h"
-#include "chrome/browser/permissions/permission_request_manager.h"
 #endif
 
 using content::BrowserThread;
@@ -174,12 +173,13 @@
     return;
   }
 
-#if defined(OS_ANDROID)
-  bool promptable = InfoBarService::FromWebContents(web_contents()) != nullptr;
-#else
-  bool promptable =
-      PermissionRequestManager::FromWebContents(web_contents()) != nullptr;
-#endif
+  bool promptable;
+  if (PermissionRequestManager::IsEnabled()) {
+    promptable =
+        PermissionRequestManager::FromWebContents(web_contents()) != nullptr;
+  } else {
+    promptable = InfoBarService::FromWebContents(web_contents()) != nullptr;
+  }
 
   // See PromptUserForDownload(): if there's no InfoBarService, then
   // DOWNLOADS_NOT_ALLOWED is functionally equivalent to PROMPT_BEFORE_DOWNLOAD.
@@ -208,19 +208,21 @@
   if (is_showing_prompt())
     return;
 
-#if defined(OS_ANDROID)
-  DownloadRequestInfoBarDelegateAndroid::Create(
-      InfoBarService::FromWebContents(web_contents_), factory_.GetWeakPtr());
-#else
-  PermissionRequestManager* permission_request_manager =
-      PermissionRequestManager::FromWebContents(web_contents_);
-  if (permission_request_manager) {
-    permission_request_manager->AddRequest(
-        new DownloadPermissionRequest(factory_.GetWeakPtr()));
+  if (PermissionRequestManager::IsEnabled()) {
+    PermissionRequestManager* permission_request_manager =
+        PermissionRequestManager::FromWebContents(web_contents_);
+    if (permission_request_manager) {
+      permission_request_manager->AddRequest(
+          new DownloadPermissionRequest(factory_.GetWeakPtr()));
+    } else {
+      Cancel();
+    }
   } else {
-    Cancel();
-  }
+#if defined(OS_ANDROID)
+    DownloadRequestInfoBarDelegateAndroid::Create(
+        InfoBarService::FromWebContents(web_contents_), factory_.GetWeakPtr());
 #endif
+  }
 }
 
 void DownloadRequestLimiter::TabDownloadState::SetContentSetting(
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
index c4ecd24..a37ce1c 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
@@ -42,20 +42,12 @@
   return permission_prompt_->GetContentSettingType(position);
 }
 
-int GroupedPermissionInfoBarDelegate::GetIconIdForPermission(
-    size_t position) const {
-  return permission_prompt_->GetIconIdForPermission(position);
-}
-
-base::string16 GroupedPermissionInfoBarDelegate::GetMessageTextFragment(
-    size_t position) const {
-  return permission_prompt_->GetMessageTextFragment(position);
+int GroupedPermissionInfoBarDelegate::GetIconId() const {
+  return permission_prompt_->GetIconId();
 }
 
 base::string16 GroupedPermissionInfoBarDelegate::GetMessageText() const {
-  return l10n_util::GetStringFUTF16(
-      IDS_PERMISSIONS_BUBBLE_PROMPT,
-      url_formatter::FormatUrlForSecurityDisplay(requesting_origin_));
+  return permission_prompt_->GetMessageText();
 }
 
 bool GroupedPermissionInfoBarDelegate::Accept() {
@@ -105,20 +97,11 @@
 }
 
 int GroupedPermissionInfoBarDelegate::GetButtons() const {
-  // If there is only one permission in the infobar, we show both OK and CANCEL
-  // button to allow/deny it. If there are multiple, we only show OK button
-  // which means making decision for all permissions according to each accept
-  // toggle.
-  return (PermissionCount() > 1) ? BUTTON_OK : (BUTTON_OK | BUTTON_CANCEL);
+  return BUTTON_OK | BUTTON_CANCEL;
 }
 
 base::string16 GroupedPermissionInfoBarDelegate::GetButtonLabel(
     InfoBarButton button) const {
-  if (PermissionCount() > 1) {
-    return l10n_util::GetStringUTF16((button == BUTTON_OK) ? IDS_APP_OK
-                                                           : IDS_APP_CANCEL);
-  }
-
   return l10n_util::GetStringUTF16((button == BUTTON_OK) ? IDS_PERMISSION_ALLOW
                                                          : IDS_PERMISSION_DENY);
 }
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
index 4bb31f5..514843d 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
@@ -17,7 +17,8 @@
 
 // An InfoBar that displays a group of permission requests, each of which can be
 // allowed or blocked independently.
-// TODO(tsergeant): Expand this class so it can be used without subclassing.
+// TODO(timloh): This is incorrectly named as we've removed grouped permissions,
+// rename it to PermissionInfoBarDelegate once crbug.com/606138 is done.
 class GroupedPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
   // Public so we can have std::unique_ptr<GroupedPermissionInfoBarDelegate>.
@@ -36,10 +37,9 @@
   bool ShouldShowPersistenceToggle() const;
 
   ContentSettingsType GetContentSettingType(size_t position) const;
-  int GetIconIdForPermission(size_t position) const;
 
-  // Message text to display for an individual permission at |position|.
-  base::string16 GetMessageTextFragment(size_t position) const;
+  // InfoBarDelegate:
+  int GetIconId() const override;
 
   // ConfirmInfoBarDelegate:
   base::string16 GetMessageText() const override;
diff --git a/chrome/browser/permissions/permission_dialog_delegate.cc b/chrome/browser/permissions/permission_dialog_delegate.cc
index 52eb39d..c3962c7 100644
--- a/chrome/browser/permissions/permission_dialog_delegate.cc
+++ b/chrome/browser/permissions/permission_dialog_delegate.cc
@@ -157,9 +157,8 @@
   j_delegate_.Reset(Java_PermissionDialogDelegate_create(
       env, reinterpret_cast<uintptr_t>(this), tab_->GetJavaObject(),
       base::android::ToJavaIntArray(env, content_settings_types).obj(),
-      ResourceMapper::MapFromChromiumId(
-          permission_prompt_->GetIconIdForPermission(0)),
-      ConvertUTF16ToJavaString(env, permission_prompt_->GetMessageText(0)),
+      ResourceMapper::MapFromChromiumId(permission_prompt_->GetIconId()),
+      ConvertUTF16ToJavaString(env, permission_prompt_->GetMessageText()),
       ConvertUTF16ToJavaString(env, permission_prompt_->GetLinkText()),
       primaryButtonText, secondaryButtonText,
       permission_prompt_->ShouldShowPersistenceToggle()));
diff --git a/chrome/browser/permissions/permission_prompt_android.cc b/chrome/browser/permissions/permission_prompt_android.cc
index e860404..7e47372 100644
--- a/chrome/browser/permissions/permission_prompt_android.cc
+++ b/chrome/browser/permissions/permission_prompt_android.cc
@@ -10,7 +10,10 @@
 #include "chrome/browser/permissions/permission_dialog_delegate.h"
 #include "chrome/browser/permissions/permission_request.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/elide_url.h"
 #include "ui/base/l10n/l10n_util.h"
 
 PermissionPromptAndroid::PermissionPromptAndroid(
@@ -108,23 +111,39 @@
   return requests[position]->GetContentSettingsType();
 }
 
-int PermissionPromptAndroid::GetIconIdForPermission(size_t position) const {
-  const std::vector<PermissionRequest*>& requests = delegate_->Requests();
-  DCHECK_LT(position, requests.size());
-  return requests[position]->GetIconId();
+// Grouped permission requests can only be Mic+Camera or Camera+Mic
+static void CheckValidRequestGroup(
+    const std::vector<PermissionRequest*>& requests) {
+  DCHECK_EQ(static_cast<size_t>(2), requests.size());
+  DCHECK_EQ(requests[0]->GetOrigin(), requests[1]->GetOrigin());
+  DCHECK((requests[0]->GetPermissionRequestType() ==
+              PermissionRequestType::PERMISSION_MEDIASTREAM_MIC &&
+          requests[1]->GetPermissionRequestType() ==
+              PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA) ||
+         (requests[0]->GetPermissionRequestType() ==
+              PermissionRequestType::PERMISSION_MEDIASTREAM_CAMERA &&
+          requests[1]->GetPermissionRequestType() ==
+              PermissionRequestType::PERMISSION_MEDIASTREAM_MIC));
 }
 
-base::string16 PermissionPromptAndroid::GetMessageText(size_t position) const {
+int PermissionPromptAndroid::GetIconId() const {
   const std::vector<PermissionRequest*>& requests = delegate_->Requests();
-  DCHECK_LT(position, requests.size());
-  return requests[position]->GetMessageText();
+  if (requests.size() == 1)
+    return requests[0]->GetIconId();
+  CheckValidRequestGroup(requests);
+  return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
 }
 
-base::string16 PermissionPromptAndroid::GetMessageTextFragment(
-    size_t position) const {
+base::string16 PermissionPromptAndroid::GetMessageText() const {
   const std::vector<PermissionRequest*>& requests = delegate_->Requests();
-  DCHECK_LT(position, requests.size());
-  return requests[position]->GetMessageTextFragment();
+  if (requests.size() == 1)
+    return requests[0]->GetMessageText();
+  CheckValidRequestGroup(requests);
+  return l10n_util::GetStringFUTF16(
+      IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO,
+      url_formatter::FormatUrlForSecurityDisplay(
+          requests[0]->GetOrigin(),
+          url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
 }
 
 base::string16 PermissionPromptAndroid::GetLinkText() const {
diff --git a/chrome/browser/permissions/permission_prompt_android.h b/chrome/browser/permissions/permission_prompt_android.h
index 7a92397..7e164f88 100644
--- a/chrome/browser/permissions/permission_prompt_android.h
+++ b/chrome/browser/permissions/permission_prompt_android.h
@@ -35,12 +35,13 @@
   void Accept();
   void Deny();
 
+  // We show one permission at a time except for grouped mic+camera, for which
+  // we still have a single icon and message text.
   size_t PermissionCount() const;
   bool ShouldShowPersistenceToggle() const;
   ContentSettingsType GetContentSettingType(size_t position) const;
-  int GetIconIdForPermission(size_t position) const;
-  base::string16 GetMessageText(size_t position) const;
-  base::string16 GetMessageTextFragment(size_t position) const;
+  int GetIconId() const;
+  base::string16 GetMessageText() const;
 
   base::string16 GetLinkText() const;
   GURL GetLinkURL() const;
diff --git a/chrome/browser/permissions/permission_request_impl.cc b/chrome/browser/permissions/permission_request_impl.cc
index 01016af..11276f7 100644
--- a/chrome/browser/permissions/permission_request_impl.cc
+++ b/chrome/browser/permissions/permission_request_impl.cc
@@ -102,7 +102,6 @@
 
 #if defined(OS_ANDROID)
 base::string16 PermissionRequestImpl::GetMessageText() const {
-  // This is currently only used for modal dialogs on Android.
   int message_id;
   switch (content_settings_type_) {
     case CONTENT_SETTINGS_TYPE_GEOLOCATION:
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 7ddb486..28215907 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -16,6 +16,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/translate/core/common/language_detection_details.h"
 #include "content/public/browser/notification_service.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
 class TranslateManagerBrowserTest : public InProcessBrowserTest {
@@ -24,11 +26,53 @@
   ~TranslateManagerBrowserTest() override {}
 
   void WaitUntilLanguageDetected() { language_detected_signal_->Wait(); }
+  void WaitUntilPageTranslated() { page_translated_signal_->Wait(); }
 
   void ResetObserver() {
     language_detected_signal_.reset(new LangageDetectionObserver(
         chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
         content::NotificationService::AllSources()));
+    page_translated_signal_.reset(new content::WindowedNotificationObserver(
+        chrome::NOTIFICATION_PAGE_TRANSLATED,
+        content::NotificationService::AllSources()));
+  }
+
+  void SimulateURLFetch(bool success) {
+    net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+    ASSERT_TRUE(fetcher);
+    net::Error error = success ? net::OK : net::ERR_FAILED;
+
+    std::string script =
+        " var google = {};"
+        "google.translate = (function() {"
+        "  return {"
+        "    TranslateService: function() {"
+        "      return {"
+        "        isAvailable : function() {"
+        "          return true;"
+        "        },"
+        "        restore : function() {"
+        "          return;"
+        "        },"
+        "        getDetectedLanguage : function() {"
+        "          return \"fr\";"
+        "        },"
+        "        translatePage : function(originalLang, targetLang,"
+        "                                 onTranslateProgress) {"
+        "          var error = (originalLang == 'auto') ? true : false;"
+        "          onTranslateProgress(100, true, error);"
+        "        }"
+        "      };"
+        "    }"
+        "  };"
+        "})();"
+        "cr.googleTranslate.onTranslateElementLoad();";
+
+    fetcher->set_url(fetcher->GetOriginalURL());
+    fetcher->set_status(net::URLRequestStatus::FromError(error));
+    fetcher->set_response_code(success ? 200 : 500);
+    fetcher->SetResponseString(script);
+    fetcher->delegate()->OnURLFetchComplete(fetcher);
   }
 
  protected:
@@ -42,11 +86,15 @@
   }
 
  private:
+  net::TestURLFetcherFactory url_fetcher_factory_;
+
   using LangageDetectionObserver =
       ui_test_utils::WindowedNotificationObserverWithDetails<
           translate::LanguageDetectionDetails>;
 
   std::unique_ptr<LangageDetectionObserver> language_detected_signal_;
+  std::unique_ptr<content::WindowedNotificationObserver>
+      page_translated_signal_;
 };
 
 // Tests that the CLD (Compact Language Detection) works properly.
@@ -88,6 +136,93 @@
             chrome_translate_client->GetLanguageState().original_language());
 }
 
+// Test that the translation was successful.
+IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest, PageTranslationSuccess) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  content::WebContents* current_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ChromeTranslateClient* chrome_translate_client =
+      ChromeTranslateClient::FromWebContents(current_web_contents);
+
+  // There is a possible race condition, when the language is not yet detected,
+  // so we check for that and wait if necessary.
+  if (chrome_translate_client->GetLanguageState().original_language().empty())
+    WaitUntilLanguageDetected();
+
+  EXPECT_EQ("und",
+            chrome_translate_client->GetLanguageState().original_language());
+
+  // Open a new tab with a page in French.
+  ResetObserver();
+  AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
+                ui::PAGE_TRANSITION_TYPED);
+  current_web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  chrome_translate_client =
+      ChromeTranslateClient::FromWebContents(current_web_contents);
+  WaitUntilLanguageDetected();
+
+  EXPECT_EQ("fr",
+            chrome_translate_client->GetLanguageState().original_language());
+
+  // Translate the page through TranslateManager.
+  translate::TranslateManager* manager =
+      chrome_translate_client->GetTranslateManager();
+  manager->TranslatePage(
+      chrome_translate_client->GetLanguageState().original_language(), "en",
+      true);
+
+  SimulateURLFetch(true);
+
+  // Wait for NOTIFICATION_PAGE_TRANSLATED notification.
+  WaitUntilPageTranslated();
+
+  EXPECT_FALSE(chrome_translate_client->GetLanguageState().translation_error());
+}
+
+// Test if there was an error during translation.
+IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest, PageTranslationError) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  content::WebContents* current_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ChromeTranslateClient* chrome_translate_client =
+      ChromeTranslateClient::FromWebContents(current_web_contents);
+
+  // There is a possible race condition, when the language is not yet detected,
+  // so we check for that and wait if necessary.
+  if (chrome_translate_client->GetLanguageState().original_language().empty())
+    WaitUntilLanguageDetected();
+
+  EXPECT_EQ("und",
+            chrome_translate_client->GetLanguageState().original_language());
+
+  // Open a new tab with about:blank page.
+  ResetObserver();
+  AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
+  current_web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  chrome_translate_client =
+      ChromeTranslateClient::FromWebContents(current_web_contents);
+  WaitUntilLanguageDetected();
+
+  EXPECT_EQ("und",
+            chrome_translate_client->GetLanguageState().original_language());
+
+  // Translate the page through TranslateManager.
+  translate::TranslateManager* manager =
+      chrome_translate_client->GetTranslateManager();
+  manager->TranslatePage(
+      chrome_translate_client->GetLanguageState().original_language(), "en",
+      true);
+
+  SimulateURLFetch(true);
+
+  // Wait for NOTIFICATION_PAGE_TRANSLATED notification.
+  WaitUntilPageTranslated();
+
+  EXPECT_TRUE(chrome_translate_client->GetLanguageState().translation_error());
+}
+
 // Test that session restore restores the translate infobar and other translate
 // settings.
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest,
diff --git a/chrome/browser/ui/android/infobars/grouped_permission_infobar.cc b/chrome/browser/ui/android/infobars/grouped_permission_infobar.cc
index 98f63aa..a799c173 100644
--- a/chrome/browser/ui/android/infobars/grouped_permission_infobar.cc
+++ b/chrome/browser/ui/android/infobars/grouped_permission_infobar.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/android/resource_mapper.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/permissions/grouped_permission_infobar_delegate_android.h"
-#include "jni/GroupedPermissionInfoBar_jni.h"
+#include "jni/PermissionInfoBar_jni.h"
 
 GroupedPermissionInfoBar::GroupedPermissionInfoBar(
     std::unique_ptr<GroupedPermissionInfoBarDelegate> delegate)
@@ -24,7 +24,7 @@
   // inform it of the toggle state.
   GroupedPermissionInfoBarDelegate* delegate = GetDelegate();
   if (delegate->ShouldShowPersistenceToggle()) {
-    delegate->set_persist(Java_GroupedPermissionInfoBar_isPersistSwitchOn(
+    delegate->set_persist(Java_PermissionInfoBar_isPersistSwitchOn(
         base::android::AttachCurrentThread(), GetJavaInfoBar()));
   }
 
@@ -47,24 +47,19 @@
       base::android::ConvertUTF16ToJavaString(
           env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_CANCEL));
 
-  std::vector<base::string16> permission_strings;
-  std::vector<int> permission_icons;
-  std::vector<int> content_settings_types;
+  int permission_icon =
+      ResourceMapper::MapFromChromiumId(delegate->GetIconId());
 
+  std::vector<int> content_settings_types;
   for (size_t i = 0; i < delegate->PermissionCount(); i++) {
-    permission_strings.push_back(delegate->GetMessageTextFragment(i));
-    permission_icons.push_back(
-        ResourceMapper::MapFromChromiumId(delegate->GetIconIdForPermission(i)));
     content_settings_types.push_back(delegate->GetContentSettingType(i));
   }
 
-  return Java_GroupedPermissionInfoBar_create(
-      env, GetTab()->GetJavaObject(),
-      base::android::ToJavaIntArray(env, content_settings_types), message_text,
+  return Java_PermissionInfoBar_create(
+      env, GetTab()->GetJavaObject(), permission_icon, nullptr, message_text,
       link_text, ok_button_text, cancel_button_text,
-      delegate->ShouldShowPersistenceToggle(),
-      base::android::ToJavaArrayOfStrings(env, permission_strings),
-      base::android::ToJavaIntArray(env, permission_icons));
+      base::android::ToJavaIntArray(env, content_settings_types),
+      delegate->ShouldShowPersistenceToggle());
 }
 
 GroupedPermissionInfoBarDelegate* GroupedPermissionInfoBar::GetDelegate() {
diff --git a/chrome/browser/ui/android/infobars/grouped_permission_infobar.h b/chrome/browser/ui/android/infobars/grouped_permission_infobar.h
index 0a2de0ea..31f4b0c 100644
--- a/chrome/browser/ui/android/infobars/grouped_permission_infobar.h
+++ b/chrome/browser/ui/android/infobars/grouped_permission_infobar.h
@@ -12,6 +12,8 @@
 
 class GroupedPermissionInfoBarDelegate;
 
+// TODO(timloh): This is incorrectly named as we've removed grouped permissions,
+// rename it to PermissionInfoBar once crbug.com/606138 is done.
 class GroupedPermissionInfoBar : public ConfirmInfoBar {
  public:
   explicit GroupedPermissionInfoBar(
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 72c580bf..da15e24 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -862,6 +862,8 @@
   if (chrome_translate_client) {
     if (chrome_translate_client->GetLanguageState().translation_pending())
       step = translate::TRANSLATE_STEP_TRANSLATING;
+    else if (chrome_translate_client->GetLanguageState().translation_error())
+      step = translate::TRANSLATE_STEP_TRANSLATE_ERROR;
     else if (chrome_translate_client->GetLanguageState().IsPageTranslated())
       step = translate::TRANSLATE_STEP_AFTER_TRANSLATE;
   }
diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc
index ad0e3e6..7c4d012 100644
--- a/chrome/browser/ui/views/infobars/infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_view.cc
@@ -280,7 +280,7 @@
           ? IDS_ACCNAME_INFOBAR_WARNING
           : IDS_ACCNAME_INFOBAR_PAGE_ACTION));
   node_data->role = ui::AX_ROLE_ALERT;
-  node_data->AddStringAttribute(ui::AX_ATTR_SHORTCUT, "Alt+Shift+A");
+  node_data->AddStringAttribute(ui::AX_ATTR_KEY_SHORTCUTS, "Alt+Shift+A");
 }
 
 gfx::Size InfoBarView::CalculatePreferredSize() const {
diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc
index 9b31d03..d5eeb0e 100644
--- a/chromecast/media/audio/cast_audio_output_stream.cc
+++ b/chromecast/media/audio/cast_audio_output_stream.cc
@@ -52,6 +52,7 @@
         buffer_duration_(audio_params.GetBufferDuration()),
         first_start_(true),
         push_in_progress_(false),
+        encountered_error_(false),
         decoder_(nullptr),
         source_callback_(nullptr),
         weak_factory_(this) {
@@ -150,7 +151,7 @@
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     DCHECK(push_in_progress_);
 
-    if (!source_callback_) {
+    if (!source_callback_ || encountered_error_) {
       push_in_progress_ = false;
       return;
     }
@@ -187,7 +188,7 @@
     DCHECK(push_in_progress_);
     push_in_progress_ = false;
 
-    if (!source_callback_)
+    if (!source_callback_ || encountered_error_)
       return;
 
     if (status != MediaPipelineBackend::kBufferSuccess) {
@@ -215,6 +216,8 @@
   void OnDecoderError() override {
     VLOG(1) << this << ": " << __func__;
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+    encountered_error_ = true;
     if (source_callback_)
       source_callback_->OnError();
   }
@@ -232,6 +235,7 @@
   const base::TimeDelta buffer_duration_;
   bool first_start_;
   bool push_in_progress_;
+  bool encountered_error_;
   base::TimeTicks next_push_time_;
   std::unique_ptr<TaskRunnerImpl> backend_task_runner_;
   std::unique_ptr<MediaPipelineBackend> backend_;
diff --git a/chromecast/media/audio/cast_audio_output_stream_unittest.cc b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
index f055c95..1a66c0293 100644
--- a/chromecast/media/audio/cast_audio_output_stream_unittest.cc
+++ b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
@@ -95,9 +95,8 @@
       case PIPELINE_STATUS_ERROR:
         return MediaPipelineBackend::kBufferFailed;
       case PIPELINE_STATUS_ASYNC_ERROR:
-        pending_push_ = true;
         delegate_->OnDecoderError();
-        return MediaPipelineBackend::kBufferPending;
+        return MediaPipelineBackend::kBufferSuccess;
       default:
         NOTREACHED();
         return MediaPipelineBackend::kBufferFailed;
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index ed93da6..8120e2a 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -6,6 +6,17 @@
   import("//build/config/android/rules.gni")
 }
 
+# Split into its own target to allow the Identity Service to depend on it in
+# typemaps without introducing a dependency on all of
+# //components/signin/core/browser, which is undesirable. In the long term
+# this file will move to be part of the Identity Service client library.
+static_library("account_info") {
+  sources = [
+    "account_info.cc",
+    "account_info.h",
+  ]
+}
+
 static_library("browser") {
   sources = [
     "about_signin_internals.cc",
@@ -14,8 +25,6 @@
     "access_token_fetcher.h",
     "account_fetcher_service.cc",
     "account_fetcher_service.h",
-    "account_info.cc",
-    "account_info.h",
     "account_info_fetcher.cc",
     "account_info_fetcher.h",
     "account_investigator.cc",
@@ -79,6 +88,7 @@
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   public_deps = [
+    ":account_info",
     "//components/signin/core/account_id",
     "//components/signin/core/common",
   ]
diff --git a/components/translate/core/browser/language_state.cc b/components/translate/core/browser/language_state.cc
index d7b65f81..1b4205b 100644
--- a/components/translate/core/browser/language_state.cc
+++ b/components/translate/core/browser/language_state.cc
@@ -14,6 +14,7 @@
       translate_driver_(driver),
       page_needs_translation_(false),
       translation_pending_(false),
+      translation_error_(false),
       translation_declined_(false),
       in_page_navigation_(false),
       translate_enabled_(false) {
@@ -45,6 +46,7 @@
   SetIsPageTranslated(false);
 
   translation_pending_ = false;
+  translation_error_ = false;
   translation_declined_ = false;
 
   SetTranslateEnabled(false);
diff --git a/components/translate/core/browser/language_state.h b/components/translate/core/browser/language_state.h
index 08e670f..10b07400 100644
--- a/components/translate/core/browser/language_state.h
+++ b/components/translate/core/browser/language_state.h
@@ -59,6 +59,10 @@
   bool translation_pending() const { return translation_pending_; }
   void set_translation_pending(bool value) { translation_pending_ = value; }
 
+  // Whether an error occured during translation.
+  bool translation_error() const { return translation_error_; }
+  void set_translation_error(bool value) { translation_error_ = value; }
+
   // Whether the user has already declined to translate the page.
   bool translation_declined() const { return translation_declined_; }
   void set_translation_declined(bool value) { translation_declined_ = value; }
@@ -109,6 +113,9 @@
   //                then we can get rid of that state.
   bool translation_pending_;
 
+  // Whether an error occured during translation.
+  bool translation_error_;
+
   // Whether the user has declined to translate the page (by closing the infobar
   // for example).  This is necessary as a new infobar could be shown if a new
   // load happens in the page after the user closed the infobar.
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 089c6137c..f38b7a7 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -427,8 +427,11 @@
 void TranslateManager::PageTranslated(const std::string& source_lang,
                                       const std::string& target_lang,
                                       TranslateErrors::Type error_type) {
-  language_state_.SetCurrentLanguage(target_lang);
+  if (error_type == TranslateErrors::NONE)
+    language_state_.SetCurrentLanguage(target_lang);
+
   language_state_.set_translation_pending(false);
+  language_state_.set_translation_error(error_type != TranslateErrors::NONE);
 
   if ((error_type == TranslateErrors::NONE) &&
       source_lang != translate::kUnknownLanguageCode &&
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index c20cff8..38f3f67 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -595,18 +595,7 @@
   if (!owner())
     return E_FAIL;
 
-  if (!acc_key)
-    return E_INVALIDARG;
-
-  BrowserAccessibilityComWin* target = GetTargetFromChildID(var_id);
-  if (!target)
-    return E_INVALIDARG;
-
-  if (target->HasStringAttribute(ui::AX_ATTR_KEY_SHORTCUTS)) {
-    return target->GetStringAttributeAsBstr(ui::AX_ATTR_KEY_SHORTCUTS, acc_key);
-  }
-
-  return target->GetStringAttributeAsBstr(ui::AX_ATTR_SHORTCUT, acc_key);
+  return AXPlatformNodeWin::get_accKeyboardShortcut(var_id, acc_key);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_accName(VARIANT var_id,
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index c2b000cf..bfd2e2c5 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -766,6 +766,13 @@
   return it->second.count;
 }
 
+void ServiceWorkerContextCore::OnStorageWiped() {
+  if (!observer_list_)
+    return;
+  observer_list_->Notify(FROM_HERE,
+                         &ServiceWorkerContextCoreObserver::OnStorageWiped);
+}
+
 void ServiceWorkerContextCore::OnRunningStateChanged(
     ServiceWorkerVersion* version) {
   if (!observer_list_)
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 0e43b1f..29120b0 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -126,6 +126,8 @@
       ServiceWorkerContextWrapper* wrapper);
   ~ServiceWorkerContextCore() override;
 
+  void OnStorageWiped();
+
   // ServiceWorkerVersion::Listener overrides.
   void OnRunningStateChanged(ServiceWorkerVersion* version) override;
   void OnVersionStateChanged(ServiceWorkerVersion* version) override;
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 011e8b3f..80155bb 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -104,7 +104,7 @@
 
 ServiceWorkerContextWrapper::ServiceWorkerContextWrapper(
     BrowserContext* browser_context)
-    : observer_list_(
+    : core_observer_list_(
           new base::ObserverListThreadSafe<ServiceWorkerContextCoreObserver>()),
       process_manager_(new ServiceWorkerProcessManager(browser_context)),
       is_incognito_(false),
@@ -740,12 +740,12 @@
 
 void ServiceWorkerContextWrapper::AddObserver(
     ServiceWorkerContextCoreObserver* observer) {
-  observer_list_->AddObserver(observer);
+  core_observer_list_->AddObserver(observer);
 }
 
 void ServiceWorkerContextWrapper::RemoveObserver(
     ServiceWorkerContextCoreObserver* observer) {
-  observer_list_->RemoveObserver(observer);
+  core_observer_list_->RemoveObserver(observer);
 }
 
 bool ServiceWorkerContextWrapper::OriginHasForeignFetchRegistrations(
@@ -781,7 +781,8 @@
   }
   context_core_.reset(new ServiceWorkerContextCore(
       user_data_directory, std::move(database_task_manager), disk_cache_thread,
-      quota_manager_proxy, special_storage_policy, observer_list_.get(), this));
+      quota_manager_proxy, special_storage_policy, core_observer_list_.get(),
+      this));
 }
 
 void ServiceWorkerContextWrapper::ShutdownOnIO() {
@@ -821,9 +822,7 @@
   }
   context_core_.reset(new ServiceWorkerContextCore(context_core_.get(), this));
   DVLOG(1) << "Restarted ServiceWorkerContextCore successfully.";
-
-  observer_list_->Notify(FROM_HERE,
-                         &ServiceWorkerContextCoreObserver::OnStorageWiped);
+  context_core_->OnStorageWiped();
 }
 
 void ServiceWorkerContextWrapper::BindWorkerFetchContext(
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index fc17febb..961d5da1 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -286,7 +286,7 @@
 
   const scoped_refptr<
       base::ObserverListThreadSafe<ServiceWorkerContextCoreObserver>>
-      observer_list_;
+      core_observer_list_;
   const std::unique_ptr<ServiceWorkerProcessManager> process_manager_;
   // Cleared in ShutdownOnIO():
   std::unique_ptr<ServiceWorkerContextCore> context_core_;
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 923e639..a2ccde49 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -560,8 +560,9 @@
         dst->AddStringAttribute(ui::AX_ATTR_LANGUAGE, src.Language().Utf8());
     }
 
-    if (src.KeyboardShortcut().length()) {
-      dst->AddStringAttribute(ui::AX_ATTR_SHORTCUT,
+    if (src.KeyboardShortcut().length() &&
+        !dst->HasStringAttribute(ui::AX_ATTR_KEY_SHORTCUTS)) {
+      dst->AddStringAttribute(ui::AX_ATTR_KEY_SHORTCUTS,
                               src.KeyboardShortcut().Utf8());
     }
 
diff --git a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
index 1d62713..ef58f0a 100644
--- a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/bind_helpers.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_adapter.h"
@@ -1459,7 +1460,7 @@
   EXPECT_EQ(-60, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   std::vector<std::string> uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
 
   discovery_sessions_[0]->Stop(
       base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)),
@@ -1587,8 +1588,8 @@
   EXPECT_EQ(-65, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   auto uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1002"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1002"));
 
   discovery_sessions_[0]->Stop(
       base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)),
@@ -1691,7 +1692,7 @@
   EXPECT_EQ(-65, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   auto uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1002"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1002"));
 
   discovery_sessions_[0]->Stop(
       base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)),
@@ -1769,7 +1770,7 @@
   EXPECT_EQ(-60, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   std::vector<std::string> uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
 
   discovery_sessions_[0]->Stop(
       base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)),
@@ -1847,26 +1848,26 @@
       EXPECT_EQ(-85, *filter->rssi);
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
     } else if (i == 1) {
       auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter();
       EXPECT_EQ("le", *filter->transport);
       EXPECT_EQ(-85, *filter->rssi);
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     } else if (i == 2) {
       auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter();
       EXPECT_EQ("le", *filter->transport);
       EXPECT_EQ(-85, *filter->rssi);
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     }
   }
 
@@ -1892,10 +1893,10 @@
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
       EXPECT_EQ(3UL, uuids.size());
-      EXPECT_EQ(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_FALSE(base::ContainsValue(uuids, "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     } else if (i == 1) {
       auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter();
       EXPECT_EQ("le", *filter->transport);
@@ -1903,10 +1904,10 @@
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
       EXPECT_EQ(2UL, uuids.size());
-      EXPECT_EQ(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_EQ(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_FALSE(base::ContainsValue(uuids, "1000"));
+      EXPECT_FALSE(base::ContainsValue(uuids, "1001"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     } else if (i == 2) {
       auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter();
       EXPECT_EQ("le", *filter->transport);
@@ -1969,19 +1970,19 @@
       EXPECT_EQ(-85, *filter->rssi);
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     } else if (i == 1 || i == 2) {
       auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter();
       EXPECT_EQ("le", *filter->transport);
       EXPECT_EQ(-85, *filter->rssi);
       EXPECT_EQ(nullptr, filter->pathloss.get());
       std::vector<std::string> uuids = *filter->uuids;
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-      EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+      EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
     }
   }
 
@@ -2049,7 +2050,7 @@
   EXPECT_EQ(-15, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   std::vector<std::string> uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
 
   df = new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE);
   df->SetRSSI(-60);
@@ -2070,9 +2071,9 @@
   EXPECT_EQ(-60, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
 
   BluetoothDiscoveryFilter* df3 =
       new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_CLASSIC);
@@ -2094,10 +2095,10 @@
   EXPECT_EQ(-65, *filter->rssi);
   EXPECT_EQ(nullptr, filter->pathloss.get());
   uuids = *filter->uuids;
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1000"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1001"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1003"));
-  EXPECT_NE(uuids.end(), std::find(uuids.begin(), uuids.end(), "1020"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1000"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1001"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1003"));
+  EXPECT_TRUE(base::ContainsValue(uuids, "1020"));
 
   // start additionally classic scan
   adapter_->StartDiscoverySession(
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
index 4f0243d..ae73a84f 100644
--- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -671,8 +671,7 @@
 void FakeBluetoothDeviceClient::CreateDevice(
     const dbus::ObjectPath& adapter_path,
     const dbus::ObjectPath& device_path) {
-  if (std::find(device_list_.begin(), device_list_.end(), device_path) !=
-      device_list_.end())
+  if (base::ContainsValue(device_list_, device_path))
     return;
 
   std::unique_ptr<Properties> properties(
@@ -807,8 +806,7 @@
     const dbus::ObjectPath& adapter_path,
     const IncomingDeviceProperties& props) {
   dbus::ObjectPath device_path(props.device_path);
-  if (std::find(device_list_.begin(), device_list_.end(), device_path) !=
-      device_list_.end())
+  if (base::ContainsValue(device_list_, device_path))
     return;
 
   std::unique_ptr<Properties> properties(
@@ -1821,8 +1819,7 @@
     base::Base64Encode(base::RandBytesAsString(10), &id);
     base::RemoveChars(id, "+/=", &id);
     device_path = dbus::ObjectPath(adapter_path.value() + "/dev" + id);
-  } while (std::find(device_list_.begin(), device_list_.end(), device_path) !=
-           device_list_.end());
+  } while (base::ContainsValue(device_list_, device_path));
 
   std::unique_ptr<Properties> properties(
       new Properties(base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged,
diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc
index 9d5ee20..8c503a4 100644
--- a/device/hid/hid_connection.cc
+++ b/device/hid/hid_connection.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/stl_util.h"
 #include "components/device_event_log/device_event_log.h"
 
 namespace device {
@@ -24,9 +25,7 @@
     if (report_id_ == HidConnection::kAnyReportId)
       return true;
 
-    return std::find(info.report_ids.begin(),
-                     info.report_ids.end(),
-                     report_id_) != info.report_ids.end();
+    return base::ContainsValue(info.report_ids, report_id_);
   }
 
  private:
diff --git a/media/base/cdm_callback_promise.cc b/media/base/cdm_callback_promise.cc
index f230afec..788ecde 100644
--- a/media/base/cdm_callback_promise.cc
+++ b/media/base/cdm_callback_promise.cc
@@ -11,9 +11,9 @@
 
 template <typename... T>
 CdmCallbackPromise<T...>::CdmCallbackPromise(
-    const base::Callback<void(const T&...)>& resolve_cb,
-    const PromiseRejectedCB& reject_cb)
-    : resolve_cb_(resolve_cb), reject_cb_(reject_cb) {
+    base::OnceCallback<void(const T&...)> resolve_cb,
+    PromiseRejectedCB reject_cb)
+    : resolve_cb_(std::move(resolve_cb)), reject_cb_(std::move(reject_cb)) {
   DCHECK(!resolve_cb_.is_null());
   DCHECK(!reject_cb_.is_null());
 }
@@ -30,7 +30,7 @@
 template <typename... T>
 void CdmCallbackPromise<T...>::resolve(const T&... result) {
   MarkPromiseSettled();
-  base::ResetAndReturn(&resolve_cb_).Run(result...);
+  std::move(resolve_cb_).Run(result...);
 }
 
 template <typename... T>
@@ -38,8 +38,7 @@
                                       uint32_t system_code,
                                       const std::string& error_message) {
   MarkPromiseSettled();
-  base::ResetAndReturn(&reject_cb_)
-      .Run(exception_code, system_code, error_message);
+  std::move(reject_cb_).Run(exception_code, system_code, error_message);
 }
 
 // Explicit template instantiation for the Promises needed.
diff --git a/media/base/cdm_callback_promise.h b/media/base/cdm_callback_promise.h
index 84f3db56..c81bb9d 100644
--- a/media/base/cdm_callback_promise.h
+++ b/media/base/cdm_callback_promise.h
@@ -18,16 +18,16 @@
 
 namespace media {
 
-typedef base::Callback<void(CdmPromise::Exception exception_code,
-                            uint32_t system_code,
-                            const std::string& error_message)>
+typedef base::OnceCallback<void(CdmPromise::Exception exception_code,
+                                uint32_t system_code,
+                                const std::string& error_message)>
     PromiseRejectedCB;
 
 template <typename... T>
 class MEDIA_EXPORT CdmCallbackPromise : public CdmPromiseTemplate<T...> {
  public:
-  CdmCallbackPromise(const base::Callback<void(const T&...)>& resolve_cb,
-                     const PromiseRejectedCB& reject_cb);
+  CdmCallbackPromise(base::OnceCallback<void(const T&...)> resolve_cb,
+                     PromiseRejectedCB reject_cb);
   virtual ~CdmCallbackPromise();
 
   // CdmPromiseTemplate<T> implementation.
@@ -41,7 +41,7 @@
   using CdmPromiseTemplate<T...>::MarkPromiseSettled;
   using CdmPromiseTemplate<T...>::RejectPromiseOnDestruction;
 
-  base::Callback<void(const T&...)> resolve_cb_;
+  base::OnceCallback<void(const T&...)> resolve_cb_;
   PromiseRejectedCB reject_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(CdmCallbackPromise);
diff --git a/media/base/cdm_initialized_promise.cc b/media/base/cdm_initialized_promise.cc
index 5db26a04..afd0ed72 100644
--- a/media/base/cdm_initialized_promise.cc
+++ b/media/base/cdm_initialized_promise.cc
@@ -7,23 +7,23 @@
 namespace media {
 
 CdmInitializedPromise::CdmInitializedPromise(
-    const CdmCreatedCB& cdm_created_cb,
-    const scoped_refptr<ContentDecryptionModule>& cdm)
-    : cdm_created_cb_(cdm_created_cb), cdm_(cdm) {}
+    CdmCreatedCB cdm_created_cb,
+    scoped_refptr<ContentDecryptionModule> cdm)
+    : cdm_created_cb_(std::move(cdm_created_cb)), cdm_(std::move(cdm)) {}
 
 CdmInitializedPromise::~CdmInitializedPromise() {
 }
 
 void CdmInitializedPromise::resolve() {
   MarkPromiseSettled();
-  cdm_created_cb_.Run(cdm_, "");
+  std::move(cdm_created_cb_).Run(cdm_, "");
 }
 
 void CdmInitializedPromise::reject(CdmPromise::Exception exception_code,
                                    uint32_t system_code,
                                    const std::string& error_message) {
   MarkPromiseSettled();
-  cdm_created_cb_.Run(nullptr, error_message);
+  std::move(cdm_created_cb_).Run(nullptr, error_message);
   // Usually after this |this| (and the |cdm_| within it) will be destroyed.
 }
 
diff --git a/media/base/cdm_initialized_promise.h b/media/base/cdm_initialized_promise.h
index f5b4868..f846eb4 100644
--- a/media/base/cdm_initialized_promise.h
+++ b/media/base/cdm_initialized_promise.h
@@ -21,8 +21,8 @@
 // then passes to |cdm_created_cb|.
 class MEDIA_EXPORT CdmInitializedPromise : public SimpleCdmPromise {
  public:
-  CdmInitializedPromise(const CdmCreatedCB& cdm_created_cb,
-                        const scoped_refptr<ContentDecryptionModule>& cdm);
+  CdmInitializedPromise(CdmCreatedCB cdm_created_cb,
+                        scoped_refptr<ContentDecryptionModule> cdm);
   ~CdmInitializedPromise() override;
 
   // SimpleCdmPromise implementation.
diff --git a/remoting/host/it2me/it2me_host_unittest.cc b/remoting/host/it2me/it2me_host_unittest.cc
index 8f8e7ef..8732597 100644
--- a/remoting/host/it2me/it2me_host_unittest.cc
+++ b/remoting/host/it2me/it2me_host_unittest.cc
@@ -182,6 +182,7 @@
 
   std::unique_ptr<base::MessageLoop> message_loop_;
   std::unique_ptr<base::RunLoop> run_loop_;
+  std::unique_ptr<FakeSignalStrategy> fake_bot_signal_strategy_;
 
   std::unique_ptr<ChromotingHostContext> host_context_;
   scoped_refptr<AutoThreadTaskRunner> network_task_runner_;
@@ -208,6 +209,8 @@
       base::ThreadTaskRunnerHandle::Get(), run_loop_->QuitClosure()));
   network_task_runner_ = host_context_->network_task_runner();
   ui_task_runner_ = host_context_->ui_task_runner();
+  fake_bot_signal_strategy_.reset(
+      new FakeSignalStrategy(SignalingAddress("fake_bot_jid")));
 }
 
 void It2MeHostTest::TearDown() {
@@ -268,13 +271,16 @@
   ice_config.expiration_time =
       base::Time::Now() + base::TimeDelta::FromHours(1);
 
+  auto fake_signal_strategy =
+      base::MakeUnique<FakeSignalStrategy>(SignalingAddress("fake_local_jid"));
+  fake_bot_signal_strategy_->ConnectTo(fake_signal_strategy.get());
+
   it2me_host_ = new It2MeHost();
   it2me_host_->Connect(host_context_->Copy(),
                        base::MakeUnique<base::DictionaryValue>(*policies_),
                        std::move(dialog_factory), weak_factory_.GetWeakPtr(),
-                       base::WrapUnique(new FakeSignalStrategy(
-                           SignalingAddress("fake_local_jid"))),
-                       kTestUserName, "fake_bot_jid", ice_config);
+                       std::move(fake_signal_strategy), kTestUserName,
+                       "fake_bot_jid", ice_config);
 
   base::RunLoop run_loop;
   state_change_callback_ =
diff --git a/remoting/host/register_support_host_request.cc b/remoting/host/register_support_host_request.cc
index 95c8be5..d4617d5 100644
--- a/remoting/host/register_support_host_request.cc
+++ b/remoting/host/register_support_host_request.cc
@@ -16,6 +16,7 @@
 #include "remoting/base/constants.h"
 #include "remoting/host/host_config.h"
 #include "remoting/host/host_details.h"
+#include "remoting/protocol/errors.h"
 #include "remoting/signaling/iq_sender.h"
 #include "remoting/signaling/jid_util.h"
 #include "remoting/signaling/signal_strategy.h"
@@ -42,6 +43,9 @@
 const char kRegisterQueryResultTag[] = "register-support-host-result";
 const char kSupportIdTag[] = "support-id";
 const char kSupportIdLifetimeTag[] = "support-id-lifetime";
+
+// The signaling timeout for register support host requests.
+constexpr int kRegisterRequestTimeoutInSeconds = 10;
 }
 
 RegisterSupportHostRequest::RegisterSupportHostRequest(
@@ -68,12 +72,22 @@
     SignalStrategy::State state) {
   if (state == SignalStrategy::CONNECTED) {
     DCHECK(!callback_.is_null());
-
     request_ = iq_sender_->SendIq(
         buzz::STR_SET, directory_bot_jid_,
         CreateRegistrationRequest(signal_strategy_->GetLocalAddress().jid()),
         base::Bind(&RegisterSupportHostRequest::ProcessResponse,
                    base::Unretained(this)));
+    if (!request_) {
+      std::string error_message =
+          "Error sending the register-support-host request.";
+      LOG(ERROR) << error_message;
+      CallCallback(std::string(), base::TimeDelta(), error_message);
+      return;
+    }
+
+    request_->SetTimeout(
+        base::TimeDelta::FromSeconds(kRegisterRequestTimeoutInSeconds));
+
   } else if (state == SignalStrategy::DISCONNECTED) {
     // We will reach here if signaling fails to connect.
     std::string error_message = "Signal strategy disconnected.";
@@ -143,11 +157,15 @@
                                                std::string* error_message) {
   std::ostringstream error;
 
+  if (!response) {
+    *error_message = "register-support-host request timed out.";
+    return;
+  }
+
   std::string type = response->Attr(buzz::QN_TYPE);
   if (type == buzz::STR_ERROR) {
     error << "Received error in response to heartbeat: " << response->Str();
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -155,7 +173,6 @@
   if (type != buzz::STR_RESULT) {
     error << "Received unexpect stanza of type \"" << type << "\"";
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -166,7 +183,6 @@
           << "> is missing in the host registration response: "
           << response->Str();
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -177,7 +193,6 @@
           << "> is missing in the host registration response: "
           << response->Str();
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -189,7 +204,6 @@
           << "> is missing in the host registration response: "
           << response->Str();
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -200,7 +214,6 @@
           << "> is malformed in the host registration response: "
           << response->Str();
     *error_message = error.str();
-    LOG(ERROR) << *error_message;
     return;
   }
 
@@ -215,6 +228,9 @@
   base::TimeDelta lifetime;
   std::string error_message;
   ParseResponse(response, &support_id, &lifetime, &error_message);
+  if (!error_message.empty()) {
+    LOG(ERROR) << error_message;
+  }
   CallCallback(support_id, lifetime, error_message);
 }
 
diff --git a/remoting/host/register_support_host_request_unittest.cc b/remoting/host/register_support_host_request_unittest.cc
index af99a9b..3c39e2e 100644
--- a/remoting/host/register_support_host_request_unittest.cc
+++ b/remoting/host/register_support_host_request_unittest.cc
@@ -10,10 +10,10 @@
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/observer_list.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringize_macros.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/base/test_rsa_key_pair.h"
@@ -34,6 +34,7 @@
 using testing::NotNull;
 using testing::Return;
 using testing::SaveArg;
+using testing::DeleteArg;
 
 namespace remoting {
 
@@ -71,12 +72,30 @@
   }
 
   base::MessageLoop message_loop_;
+  base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_;
   MockSignalStrategy signal_strategy_;
   base::ObserverList<SignalStrategy::Listener, true> signal_strategy_listeners_;
   scoped_refptr<RsaKeyPair> key_pair_;
   base::MockCallback<RegisterSupportHostRequest::RegisterCallback> callback_;
 };
 
+TEST_F(RegisterSupportHostRequestTest, Timeout) {
+  std::unique_ptr<RegisterSupportHostRequest> request(
+      new RegisterSupportHostRequest(&signal_strategy_, key_pair_, kTestBotJid,
+                                     callback_.Get()));
+  EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
+  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
+      .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+  request->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
+
+  // Generate response and verify that callback is called.
+  EXPECT_CALL(callback_, Run("", base::TimeDelta::FromSeconds(0),
+                             "register-support-host request timed out."));
+
+  mock_time_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(15));
+}
+
 TEST_F(RegisterSupportHostRequestTest, Send) {
   // |iq_request| is freed by RegisterSupportHostRequest.
   int64_t start_time = static_cast<int64_t>(base::Time::Now().ToDoubleT());
@@ -92,7 +111,7 @@
       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
 
   request->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
+  mock_time_task_runner_->RunUntilIdle();
 
   // Verify format of the query.
   std::unique_ptr<XmlElement> stanza(sent_iq);
@@ -167,7 +186,7 @@
   }
   EXPECT_EQ(1, consumed);
 
-  base::RunLoop().RunUntilIdle();
+  mock_time_task_runner_->RunUntilIdle();
 }
 
 }  // namespace remoting
diff --git a/services/identity/public/cpp/DEPS b/services/identity/public/cpp/DEPS
index 4b54793..9b27152 100644
--- a/services/identity/public/cpp/DEPS
+++ b/services/identity/public/cpp/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+components/signin/core/browser/account_info.h",
   "+google_apis/gaia/google_service_auth_error.h",
 ]
diff --git a/services/identity/public/cpp/account_info.typemap b/services/identity/public/cpp/account_info.typemap
new file mode 100644
index 0000000..f36dad2
--- /dev/null
+++ b/services/identity/public/cpp/account_info.typemap
@@ -0,0 +1,19 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//services/identity/public/interfaces/account_info.mojom"
+public_headers = [ "//components/signin/core/browser/account_info.h" ]
+traits_headers =
+    [ "//services/identity/public/cpp/account_info_struct_traits.h" ]
+sources = [
+  "//services/identity/public/cpp/account_info_struct_traits.cc",
+]
+public_deps = [
+  # TODO(blundell): In the long term, any files from //components/signin that
+  # are exposed to consumers of the Identity Service should move to be part of
+  # the client library of the Identity Service.
+  "//components/signin/core/browser:account_info",
+]
+
+type_mappings = [ "identity.mojom.AccountInfo=::AccountInfo" ]
diff --git a/services/identity/public/cpp/account_info_struct_traits.cc b/services/identity/public/cpp/account_info_struct_traits.cc
new file mode 100644
index 0000000..1fe4daa
--- /dev/null
+++ b/services/identity/public/cpp/account_info_struct_traits.cc
@@ -0,0 +1,51 @@
+// 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 "services/identity/public/cpp/account_info_struct_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<identity::mojom::AccountInfo::DataView, ::AccountInfo>::Read(
+    identity::mojom::AccountInfo::DataView data,
+    ::AccountInfo* out) {
+  std::string account_id;
+  std::string gaia;
+  std::string email;
+  std::string full_name;
+  std::string given_name;
+  std::string hosted_domain;
+  std::string locale;
+  std::string picture_url;
+  if (!data.ReadAccountId(&account_id) || !data.ReadGaia(&gaia) ||
+      !data.ReadEmail(&email) || !data.ReadFullName(&full_name) ||
+      !data.ReadGivenName(&given_name) ||
+      !data.ReadHostedDomain(&hosted_domain) || !data.ReadLocale(&locale) ||
+      !data.ReadPictureUrl(&picture_url)) {
+    return false;
+  }
+
+  out->account_id = account_id;
+  out->gaia = gaia;
+  out->email = email;
+  out->full_name = full_name;
+  out->given_name = given_name;
+  out->hosted_domain = hosted_domain;
+  out->locale = locale;
+  out->picture_url = picture_url;
+  out->is_child_account = data.is_child_account();
+
+  return true;
+}
+
+// static
+bool StructTraits<identity::mojom::AccountInfo::DataView,
+                  ::AccountInfo>::IsNull(const ::AccountInfo& input) {
+  // Note that an AccountInfo being null cannot be defined as
+  // !AccountInfo::IsValid(), as IsValid() requires that *every* field is
+  // populated, which is too stringent a requirement.
+  return input.account_id.empty() || input.gaia.empty() || input.email.empty();
+}
+
+}  // namespace mojo
diff --git a/services/identity/public/cpp/account_info_struct_traits.h b/services/identity/public/cpp/account_info_struct_traits.h
new file mode 100644
index 0000000..fb9ccf3
--- /dev/null
+++ b/services/identity/public/cpp/account_info_struct_traits.h
@@ -0,0 +1,57 @@
+// 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 SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNT_INFO_STRUCT_TRAITS_H_
+#define SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNT_INFO_STRUCT_TRAITS_H_
+
+#include <string>
+
+#include "components/signin/core/browser/account_info.h"
+#include "services/identity/public/interfaces/account_info.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<identity::mojom::AccountInfo::DataView, ::AccountInfo> {
+  static const std::string& account_id(const ::AccountInfo& r) {
+    return r.account_id;
+  }
+
+  static const std::string& gaia(const ::AccountInfo& r) { return r.gaia; }
+
+  static const std::string& email(const ::AccountInfo& r) { return r.email; }
+
+  static const std::string& full_name(const ::AccountInfo& r) {
+    return r.full_name;
+  }
+
+  static const std::string& given_name(const ::AccountInfo& r) {
+    return r.given_name;
+  }
+
+  static const std::string& hosted_domain(const ::AccountInfo& r) {
+    return r.hosted_domain;
+  }
+
+  static const std::string& locale(const ::AccountInfo& r) { return r.locale; }
+
+  static const std::string& picture_url(const ::AccountInfo& r) {
+    return r.picture_url;
+  }
+
+  static bool is_child_account(const ::AccountInfo& r) {
+    return r.is_child_account;
+  }
+
+  static bool Read(identity::mojom::AccountInfo::DataView data,
+                   ::AccountInfo* out);
+
+  static bool IsNull(const ::AccountInfo& input);
+
+  static void SetToNull(::AccountInfo* output) { *output = AccountInfo(); }
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNT_INFO_STRUCT_TRAITS_H_
diff --git a/services/identity/public/cpp/typemaps.gni b/services/identity/public/cpp/typemaps.gni
index 19923c73..006444b3 100644
--- a/services/identity/public/cpp/typemaps.gni
+++ b/services/identity/public/cpp/typemaps.gni
@@ -1,4 +1,5 @@
 typemaps = [
+  "//services/identity/public/cpp/account_info.typemap",
   "//services/identity/public/cpp/google_service_auth_error.typemap",
   "//services/identity/public/cpp/scope_set.typemap",
 ]
diff --git a/services/identity/public/interfaces/BUILD.gn b/services/identity/public/interfaces/BUILD.gn
index faa6bad..ae1569d 100644
--- a/services/identity/public/interfaces/BUILD.gn
+++ b/services/identity/public/interfaces/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("interfaces") {
   sources = [
+    "account_info.mojom",
     "google_service_auth_error.mojom",
     "identity_manager.mojom",
     "scope_set.mojom",
diff --git a/services/identity/public/interfaces/account_info.mojom b/services/identity/public/interfaces/account_info.mojom
new file mode 100644
index 0000000..670211c
--- /dev/null
+++ b/services/identity/public/interfaces/account_info.mojom
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module identity.mojom;
+
+// Information about a specific Google account. A valid AccountInfo will always
+// have an account ID, gaia ID, and email address. However, some or all of the
+// other fields may be empty.
+struct AccountInfo {
+  // The account ID used by OAuth2TokenService. This is an opaque identifier
+  // that represents this account within Chrome.
+  string account_id;
+  // The GAIA ID corresponding to this account.
+  string gaia;
+  // The email address corresponding to this account.
+  string email;
+  // The user's full name.
+  string full_name;
+  // The user's given name.
+  string given_name;
+  // The hosted domain used by this user, if any.
+  // TODO(blundell): Change this to mojo.common.mojom.Url when I'm able to
+  // change the corresponding definition in account_info.h to GURL.
+  string hosted_domain;
+  // The locale preferred by this user.
+  string locale;
+  // The URL giving a picture of this user.
+  // TODO(blundell): Change this to mojo.common.mojom.Url when I'm able to
+  // change the corresponding definition in account_info.h to GURL.
+  string picture_url;
+  // Whether this account is for a child.
+  bool is_child_account;
+};
diff --git a/services/preferences/persistent_pref_store_impl.cc b/services/preferences/persistent_pref_store_impl.cc
index 8df4fe6..42cbdce 100644
--- a/services/preferences/persistent_pref_store_impl.cc
+++ b/services/preferences/persistent_pref_store_impl.cc
@@ -180,6 +180,7 @@
   mojom::PrefStoreObserverPtr observer;
   mojom::PrefStoreObserverRequest observer_request =
       mojo::MakeRequest(&observer);
+  auto values = FilterPrefs(backing_pref_store_->GetValues(), observed_prefs);
   auto connection = base::MakeUnique<Connection>(
       this, mojo::MakeRequest(&pref_store_ptr), std::move(observer),
       std::move(observed_prefs));
@@ -187,7 +188,7 @@
   connections_.insert(std::make_pair(connection_ptr, std::move(connection)));
   return mojom::PersistentPrefStoreConnection::New(
       mojom::PrefStoreConnection::New(std::move(observer_request),
-                                      backing_pref_store_->GetValues(), true),
+                                      std::move(values), true),
       std::move(pref_store_ptr), backing_pref_store_->GetReadError(),
       backing_pref_store_->ReadOnly());
 }
diff --git a/services/preferences/persistent_pref_store_impl.h b/services/preferences/persistent_pref_store_impl.h
index 6c3ec98..b0313415 100644
--- a/services/preferences/persistent_pref_store_impl.h
+++ b/services/preferences/persistent_pref_store_impl.h
@@ -6,6 +6,7 @@
 #define SERVICES_PREFERENCES_PERSISTENT_PREF_STORE_IMPL_H_
 
 #include <memory>
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
diff --git a/services/preferences/persistent_pref_store_impl_unittest.cc b/services/preferences/persistent_pref_store_impl_unittest.cc
index 1c4e0b3..e25c92d 100644
--- a/services/preferences/persistent_pref_store_impl_unittest.cc
+++ b/services/preferences/persistent_pref_store_impl_unittest.cc
@@ -174,14 +174,26 @@
 }
 
 TEST_F(PersistentPrefStoreImplTest, InitialValue) {
+  constexpr char kUnregisteredKey[] = "path.to.unregistered_key";
+  constexpr char kUnregisteredTopLevelKey[] = "unregistered_key";
+  constexpr char kUnregisteredPrefixKey[] = "p";
   auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
   const base::Value value("value");
   backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
+  backing_pref_store->SetValue(kUnregisteredKey, value.CreateDeepCopy(), 0);
+  backing_pref_store->SetValue(kUnregisteredPrefixKey, value.CreateDeepCopy(),
+                               0);
+  backing_pref_store->SetValue(kUnregisteredTopLevelKey, value.CreateDeepCopy(),
+                               0);
   CreateImpl(backing_pref_store);
   EXPECT_TRUE(pref_store()->IsInitializationComplete());
   const base::Value* output = nullptr;
   ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
   EXPECT_TRUE(value.Equals(output));
+
+  EXPECT_FALSE(pref_store()->GetValue(kUnregisteredKey, nullptr));
+  EXPECT_FALSE(pref_store()->GetValue(kUnregisteredTopLevelKey, nullptr));
+  EXPECT_FALSE(pref_store()->GetValue(kUnregisteredPrefixKey, nullptr));
 }
 
 TEST_F(PersistentPrefStoreImplTest, InitialValueWithoutPathExpansion) {
diff --git a/services/preferences/public/cpp/lib/util.cc b/services/preferences/public/cpp/lib/util.cc
index a53e459..493b8aa 100644
--- a/services/preferences/public/cpp/lib/util.cc
+++ b/services/preferences/public/cpp/lib/util.cc
@@ -10,6 +10,62 @@
 #include "base/values.h"
 
 namespace prefs {
+namespace {
+
+enum class MatchType { kNo, kExact, kPrefix };
+
+MatchType MatchPref(base::StringPiece string, base::StringPiece prefix) {
+  if (string.substr(0, prefix.size()) != prefix)
+    return MatchType::kNo;
+
+  if (string.size() == prefix.size())
+    return MatchType::kExact;
+
+  return string[prefix.size()] == '.' ? MatchType::kPrefix : MatchType::kNo;
+}
+
+std::unique_ptr<base::DictionaryValue> FilterPrefsImpl(
+    std::unique_ptr<base::DictionaryValue> prefs,
+    const std::set<std::string>& observed_prefs,
+    const std::string& prefix) {
+  if (!prefs)
+    return nullptr;
+
+  auto filtered_value = base::MakeUnique<base::DictionaryValue>();
+  std::string full_path = prefix;
+  for (auto& pref : *prefs) {
+    full_path.resize(prefix.size());
+    if (full_path.empty()) {
+      full_path = pref.first;
+    } else {
+      full_path.reserve(full_path.size() + 1 + pref.first.size());
+      full_path += ".";
+      full_path += pref.first;
+    }
+    auto it = observed_prefs.lower_bound(full_path);
+    if (it == observed_prefs.end())
+      continue;
+
+    auto result = MatchPref(*it, full_path);
+    switch (result) {
+      case MatchType::kNo:
+        break;
+      case MatchType::kExact:
+        filtered_value->Set(pref.first, std::move(pref.second));
+        break;
+      case MatchType::kPrefix:
+        auto filtered_subpref =
+            FilterPrefsImpl(base::DictionaryValue::From(std::move(pref.second)),
+                            observed_prefs, full_path);
+        if (filtered_subpref)
+          filtered_value->Set(pref.first, std::move(filtered_subpref));
+        break;
+    }
+  }
+  return filtered_value;
+}
+
+}  // namespace
 
 void SetValue(base::DictionaryValue* dictionary_value,
               const std::vector<base::StringPiece>& path_components,
@@ -28,4 +84,10 @@
     dictionary_value->RemoveWithoutPathExpansion(key, nullptr);
 }
 
+std::unique_ptr<base::DictionaryValue> FilterPrefs(
+    std::unique_ptr<base::DictionaryValue> prefs,
+    const std::set<std::string>& observed_prefs) {
+  return FilterPrefsImpl(std::move(prefs), observed_prefs, std::string());
+}
+
 }  // namespace prefs
diff --git a/services/preferences/public/cpp/lib/util.h b/services/preferences/public/cpp/lib/util.h
index 3e3c88a..213497b 100644
--- a/services/preferences/public/cpp/lib/util.h
+++ b/services/preferences/public/cpp/lib/util.h
@@ -6,6 +6,8 @@
 #define SERVICES_PREFERENCES_PUBLIC_CPP_LIB_UTIL_H_
 
 #include <memory>
+#include <set>
+#include <string>
 #include <vector>
 
 #include "base/strings/string_piece.h"
@@ -23,6 +25,11 @@
               const std::vector<base::StringPiece>& path_components,
               std::unique_ptr<base::Value> value);
 
+// Filters |prefs| to paths contained within |observed_prefs|.
+std::unique_ptr<base::DictionaryValue> FilterPrefs(
+    std::unique_ptr<base::DictionaryValue> prefs,
+    const std::set<std::string>& observed_prefs);
+
 }  // namespace prefs
 
 #endif  // SERVICES_PREFERENCES_PUBLIC_CPP_LIB_UTIL_H_
diff --git a/services/preferences/public/cpp/pref_store_impl.cc b/services/preferences/public/cpp/pref_store_impl.cc
index efcf8f3..e311718 100644
--- a/services/preferences/public/cpp/pref_store_impl.cc
+++ b/services/preferences/public/cpp/pref_store_impl.cc
@@ -5,18 +5,18 @@
 #include "services/preferences/public/cpp/pref_store_impl.h"
 
 #include <memory>
-#include <unordered_set>
+#include <set>
 #include <utility>
 
 #include "base/stl_util.h"
 #include "base/values.h"
+#include "services/preferences/public/cpp/lib/util.h"
 
 namespace prefs {
 
 class PrefStoreImpl::Observer {
  public:
-  Observer(mojom::PrefStoreObserverPtr observer,
-           std::unordered_set<std::string> prefs)
+  Observer(mojom::PrefStoreObserverPtr observer, std::set<std::string> prefs)
       : observer_(std::move(observer)), prefs_(std::move(prefs)) {}
 
   void OnInitializationCompleted(bool succeeded) {
@@ -46,7 +46,7 @@
 
  private:
   mojom::PrefStoreObserverPtr observer_;
-  const std::unordered_set<std::string> prefs_;
+  const std::set<std::string> prefs_;
 
   DISALLOW_COPY_AND_ASSIGN(Observer);
 };
@@ -105,13 +105,14 @@
     AddObserverCallback callback) {
   mojom::PrefStoreObserverPtr observer_ptr;
   auto request = mojo::MakeRequest(&observer_ptr);
-  observers_.push_back(base::MakeUnique<Observer>(
-      std::move(observer_ptr),
-      std::unordered_set<std::string>(prefs_to_observe.begin(),
-                                      prefs_to_observe.end())));
+  std::set<std::string> observed_prefs(prefs_to_observe.begin(),
+                                       prefs_to_observe.end());
   std::move(callback).Run(mojom::PrefStoreConnection::New(
-      std::move(request), backing_pref_store_->GetValues(),
+      std::move(request),
+      FilterPrefs(backing_pref_store_->GetValues(), observed_prefs),
       backing_pref_store_->IsInitializationComplete()));
+  observers_.push_back(base::MakeUnique<Observer>(std::move(observer_ptr),
+                                                  std::move(observed_prefs)));
 }
 
 }  // namespace prefs
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 36f019b..4fc005b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2878,3 +2878,5 @@
 
 crbug.com/732103 [ Mac ] http/tests/shapedetection/shapedetection-cross-origin.html [ Failure Pass Timeout ]
 crbug.com/732103 [ Mac ] virtual/mojo-loading/http/tests/shapedetection/shapedetection-cross-origin.html [ Failure Pass Timeout ]
+
+crbug.com/732665 [ Android ] paint/invalidation/selected-replaced.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated-expected.txt b/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated-expected.txt
new file mode 100644
index 0000000..97c5ca2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated-expected.txt
@@ -0,0 +1,6 @@
+CONSOLE WARNING: line 20: The step timing function with step position 'middle' is deprecated and will be removed in M62, around October 2017. Please use the frames timing function instead. See https://www.chromestatus.com/features/5189363944128512 for more details.
+This is a testharness.js-based test.
+PASS The step-middle timing function does not function outside of Web Animations. 
+PASS The step-middle timing function is deprecated in Web Animations. 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated.html b/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated.html
new file mode 100644
index 0000000..eeee64d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/step-middle-keyword-timing-function-deprecated.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+// From WebFeature.h
+var kDeprecatedTimingFunctionStepMiddle = 2024;
+
+test(() => {
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+  var element = document.createElement('div');
+  element.style.animationTimingFunction = 'step-middle';
+  document.documentElement.appendChild(element);
+  assert_equals(getComputedStyle(element).animationTimingFunction, 'ease');
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+}, 'The step-middle timing function does not function outside of Web Animations.');
+
+test(() => {
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+  document.documentElement.animate([], { easing: 'step-middle' });
+  assert_true(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+}, 'The step-middle timing function is deprecated in Web Animations.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated-expected.txt b/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated-expected.txt
new file mode 100644
index 0000000..80fc9976
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated-expected.txt
@@ -0,0 +1,6 @@
+CONSOLE WARNING: line 20: The step timing function with step position 'middle' is deprecated and will be removed in M62, around October 2017. Please use the frames timing function instead. See https://www.chromestatus.com/features/5189363944128512 for more details.
+This is a testharness.js-based test.
+PASS The step timing function with step position 'middle' does not function outside of Web Animations. 
+PASS The step timing function with step position 'middle' is deprecated in Web Animations. 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated.html b/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated.html
new file mode 100644
index 0000000..bda90b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/step-middle-timing-function-deprecated.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+// From WebFeature.h
+var kDeprecatedTimingFunctionStepMiddle = 2024;
+
+test(() => {
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+  var element = document.createElement('div');
+  element.style.animationTimingFunction = 'steps(3, middle)';
+  document.documentElement.appendChild(element);
+  assert_equals(getComputedStyle(element).animationTimingFunction, 'ease');
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+}, 'The step timing function with step position \'middle\' does not function outside of Web Animations.');
+
+test(() => {
+  assert_false(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+  document.documentElement.animate([], { easing: 'steps(3, middle)' });
+  assert_true(internals.isUseCounted(document, kDeprecatedTimingFunctionStepMiddle));
+}, 'The step timing function with step position \'middle\' is deprecated in Web Animations.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/timing-functions.html b/third_party/WebKit/LayoutTests/animations/timing-functions.html
index cb03f81..be5f041 100644
--- a/third_party/WebKit/LayoutTests/animations/timing-functions.html
+++ b/third_party/WebKit/LayoutTests/animations/timing-functions.html
@@ -70,7 +70,7 @@
     }
     /*
      * The step-middle functions are invalid except through the Web Animations API
-     * and should behave like 'ease', unless step-middle has been added to the CSS specification.
+     * (where they are deprecated) and should behave like 'ease'.
      */
     #middlebox1 {
       animation-timing-function: steps(3, middle);
diff --git a/third_party/WebKit/LayoutTests/app_banner/app-banner-event-prompt.html b/third_party/WebKit/LayoutTests/app_banner/app-banner-event-prompt.html
index f44c664e..56ea1fdc1 100644
--- a/third_party/WebKit/LayoutTests/app_banner/app-banner-event-prompt.html
+++ b/third_party/WebKit/LayoutTests/app_banner/app-banner-event-prompt.html
@@ -104,7 +104,7 @@
             );
             assert_equals(
                 error.message,
-                "The prompt() method may only be called once, following preventDefault().",
+                "The prompt() method may only be called once.",
                 "Rejected promise does not provide expected message."
             );
         })
@@ -154,27 +154,17 @@
                     assert_false(event == null, "event is null outside handler");
 
                     // Test that firing prompt() outside of the handler resolves or rejects correctly.
-                    if (test_case.cancel) {
-                        verify_prompt_resolve(event, t);
-                    } else {
-                        verify_prompt_reject(event, t);
-                    }
+                    verify_prompt_resolve(event, t);
                     // Check userChoice and call the next test.
                     verify_userChoice(event, t, test_case, index);
                 }, 0);
                 return;
             }
 
-            if (test_case.cancel) {
-                // Call prompt() to restart the pipeline.
-                verify_prompt_resolve(e, t);
-            } else {
-                // A call to prompt() before preventDefault should reject.
-                verify_prompt_reject(e, t);
-            }
+            // Call prompt() to restart the pipeline.
+            verify_prompt_resolve(e, t);
 
-            // prompt() has been fired or the event has not been canceled, so check
-            // the userChoice promise and call the next test.
+            // prompt() has been fired, so check the userChoice promise and call the next test.
             verify_userChoice(e, t, test_case, index);
         });
         window.addEventListener('beforeinstallprompt', event_handler);
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-animate-rotate.html b/third_party/WebKit/LayoutTests/images/color-profile-animate-rotate.html
index 740df05..4e71e93 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-animate-rotate.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-animate-rotate.html
@@ -15,10 +15,6 @@
 
 <script>
 function rotate(element) {
-  if (window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('sRGB', new Function()) }, 0);
-
-  element.addEventListener('animationstart', start, false);
   element.addEventListener('animationend', end, false);
 
   if (window.testRunner)
@@ -27,11 +23,6 @@
     element.style.cssText += 'animation: rotate linear 4s';
 }
 
-function start(event) {
-  if (window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', new Function()) }, 100);
-}
-
 function end(event) {
   if (window.testRunner)
     setTimeout(function() { testRunner.notifyDone() }, 0);
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-animate.html b/third_party/WebKit/LayoutTests/images/color-profile-animate.html
index d500106b..04361814 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-animate.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-animate.html
@@ -22,11 +22,7 @@
 
 function end(event) {
   if (window.testRunner)
-    testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-clip-text.html b/third_party/WebKit/LayoutTests/images/color-profile-background-clip-text.html
index 711e894..4568120 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-clip-text.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-clip-text.html
@@ -27,17 +27,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cover.html b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cover.html
index b0070f9..48f9b7c 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cover.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cover.html
@@ -25,17 +25,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png.html b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png.html
index c4f00c0..51136bd 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png.html
@@ -28,17 +28,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade.html b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade.html
index 386690a..ab1c9c9 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade.html
@@ -28,17 +28,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-repeat.html b/third_party/WebKit/LayoutTests/images/color-profile-background-image-repeat.html
index 4581053..e8e2307 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-repeat.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-repeat.html
@@ -26,17 +26,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-space.html b/third_party/WebKit/LayoutTests/images/color-profile-background-image-space.html
index 13dbcbd..be0fdbd0 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-space.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-space.html
@@ -26,17 +26,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-fade.html b/third_party/WebKit/LayoutTests/images/color-profile-border-fade.html
index 542526ee..53b5567 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-fade.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-fade.html
@@ -34,7 +34,6 @@
 }
 
 if (window.testRunner) {
-  testRunner.setColorProfile('colorSpin', new Function());
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
 }
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-image-source.html b/third_party/WebKit/LayoutTests/images/color-profile-border-image-source.html
index 1d673bf..5f748f9 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-image-source.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-image-source.html
@@ -44,13 +44,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 };
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-image.html b/third_party/WebKit/LayoutTests/images/color-profile-border-image.html
index 820d2254..2baef32 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-image.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-image.html
@@ -17,17 +17,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-radius.html b/third_party/WebKit/LayoutTests/images/color-profile-border-radius.html
index 25bd4bd7..259670e 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-radius.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-radius.html
@@ -30,11 +30,7 @@
   element.classList.add('border');
 
   if (++images == 6 && window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-clip.html b/third_party/WebKit/LayoutTests/images/color-profile-clip.html
index 4da9644f..374332f 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-clip.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-clip.html
@@ -23,11 +23,7 @@
 
 function load() {
   if (++images == 3 && window.testRunner)
-    testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-drag-image.html b/third_party/WebKit/LayoutTests/images/color-profile-drag-image.html
index d4efe872..605451c3 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-drag-image.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-drag-image.html
@@ -26,20 +26,12 @@
 
   var image = document.querySelector('img');
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(drawImagePatternToCanvas);
   };
 
   image.src = 'resources/red-at-12-oclock-with-color-profile.jpg';
 };
 
-function changeColorProfile() {
-  window.testRunner.setColorProfile('colorSpin', profileChanged);
-}
-
-function profileChanged() {
-  setTimeout(drawImagePatternToCanvas, 0);
-}
-
 function drawImagePatternToCanvas() {
   var canvas = document.querySelector('canvas');
   var ctx = canvas.getContext('2d');
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-filter.html b/third_party/WebKit/LayoutTests/images/color-profile-filter.html
index 39f3fca..f0cf66e7 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-filter.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-filter.html
@@ -22,11 +22,7 @@
   element.classList.add('filter');
 
   if (++images == 6 && window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-group.html b/third_party/WebKit/LayoutTests/images/color-profile-group.html
index 8ad182a..b94c843 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-group.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-group.html
@@ -28,15 +28,7 @@
 
 <script>
 function load() {
-  runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+  runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-iframe.html b/third_party/WebKit/LayoutTests/images/color-profile-iframe.html
index 6c299dc..f84097e 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-iframe.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-iframe.html
@@ -12,15 +12,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-pattern.html b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-pattern.html
index 4ad493eb..84b0520c 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-pattern.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-pattern.html
@@ -19,20 +19,12 @@
   var image = document.querySelector('img');
 
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(drawImagePatternToCanvas);
   };
 
   image.src = 'resources/red-at-12-oclock-with-color-profile.jpg';
 };
 
-function changeColorProfile() {
-  window.testRunner.setColorProfile('colorSpin', profileChanged);
-}
-
-function profileChanged() {
-  setTimeout(drawImagePatternToCanvas, 0);
-}
-
 function drawImagePatternToCanvas() {
   var canvas = document.querySelector('canvas');
   var ctx = canvas.getContext('2d');
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-svg.html b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-svg.html
index ee18d9d0..da2a728 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-svg.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas-svg.html
@@ -20,17 +20,13 @@
   var image = document.querySelector('img');
 
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(loadedImage);
   };
 
   image.src = 'resources/color-profile-image-data-url.svg';
 };
 
-function changeColorProfile() {
-  window.testRunner.setColorProfile('sRGB', profileChanged);
-}
-
-function profileChanged() {
+function loadedImage() {
   setTimeout(drawImageToCanvas, 0, document.querySelector('img'));
 }
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas.html b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas.html
index c85e2ea..cf89af0 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-canvas.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-canvas.html
@@ -18,20 +18,12 @@
   var image = document.querySelector('img');
 
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(drawImageToCanvas);
   };
 
   image.src = 'resources/red-at-12-oclock-with-color-profile.jpg';
 };
 
-function changeColorProfile() {
-  window.testRunner.setColorProfile('sRGB', profileChanged);
-}
-
-function profileChanged() {
-  setTimeout(drawImageToCanvas, 0);
-}
-
 function drawImageToCanvas() {
   var canvas = document.querySelector('canvas');
   canvas.getContext('2d').clearRect(0, 0, canvas.width = 380, canvas.height = 380);
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all.html b/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all.html
index 0e591a3..76374e3 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all.html
@@ -72,13 +72,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 };
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit.html b/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit.html
index 0751ac02..f37dac0 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit.html
@@ -24,11 +24,7 @@
 
 function load() {
   if (++images == 2 && window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 200);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match.html b/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match.html
index 3176bd30..f84af748 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match.html
@@ -19,18 +19,7 @@
 
 function load() {
   if (++images == 3 && window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  /* The test images have an sRGB color profile and so should pass through
-   * color correction unchanged if the output device profile is sRGB.
-   */
-  testRunner.setColorProfile('sRGB', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-pseudo-content.html b/third_party/WebKit/LayoutTests/images/color-profile-image-pseudo-content.html
index 8900a80..5a6f6aa 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-pseudo-content.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-pseudo-content.html
@@ -25,17 +25,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 0);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-shape.html b/third_party/WebKit/LayoutTests/images/color-profile-image-shape.html
index 2a21a005..104155b5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-shape.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-shape.html
@@ -25,16 +25,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 20);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
 
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-svg-resource-url.html b/third_party/WebKit/LayoutTests/images/color-profile-image-svg-resource-url.html
index 06db2c55..e936fef 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-svg-resource-url.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-svg-resource-url.html
@@ -12,15 +12,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image.html b/third_party/WebKit/LayoutTests/images/color-profile-image.html
index 2fda934..ba42fec 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image.html
@@ -23,11 +23,7 @@
 
 function load() {
   if (++images == 6 && window.testRunner)
-    testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-layer-filter.html b/third_party/WebKit/LayoutTests/images/color-profile-layer-filter.html
index a22ecf6..a97264b 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-layer-filter.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-layer-filter.html
@@ -26,11 +26,7 @@
 
 function next() {
   if (++images == 3 && window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', profileChanged) }, 100);
-}
-
-function profileChanged() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-layer.html b/third_party/WebKit/LayoutTests/images/color-profile-layer.html
index e397622..26d5f9905 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-layer.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-layer.html
@@ -27,11 +27,7 @@
   element.classList.add('layer');
 
   if (++images == 6 && window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 500);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg.html b/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg.html
index 6b7753c7..14076f6 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg.html
@@ -29,15 +29,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-object.html b/third_party/WebKit/LayoutTests/images/color-profile-object.html
index 545eb27..3a39dae0 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-object.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-object.html
@@ -15,17 +15,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 };
 
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-reflection.html b/third_party/WebKit/LayoutTests/images/color-profile-reflection.html
index 00702468..21f87cb4 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-reflection.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-reflection.html
@@ -23,13 +23,9 @@
   document.querySelector('div').classList.add('reflection');
 
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
+    runAfterLayoutAndPaint(done);
 };
 
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
 function done() {
   runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text.html b/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text.html
index c4c4cab0..072517a1 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text.html
@@ -17,15 +17,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-svg-foreign-object.html b/third_party/WebKit/LayoutTests/images/color-profile-svg-foreign-object.html
index 2fa214e55..1c1982c 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-svg-foreign-object.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-svg-foreign-object.html
@@ -12,15 +12,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-svg.html b/third_party/WebKit/LayoutTests/images/color-profile-svg.html
index 5e11a4a..0accb31 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-svg.html
+++ b/third_party/WebKit/LayoutTests/images/color-profile-svg.html
@@ -19,15 +19,7 @@
 <script>
 function load() {
   if (window.testRunner)
-    runAfterLayoutAndPaint(changeColorProfile);
-}
-
-function changeColorProfile() {
-  testRunner.setColorProfile('colorSpin', done);
-}
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 }
 
 if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/images/resources/color-checker-munsell-chart.js b/third_party/WebKit/LayoutTests/images/resources/color-checker-munsell-chart.js
index 2f494b3..384f04c 100644
--- a/third_party/WebKit/LayoutTests/images/resources/color-checker-munsell-chart.js
+++ b/third_party/WebKit/LayoutTests/images/resources/color-checker-munsell-chart.js
@@ -6,23 +6,12 @@
   var image = document.querySelector('img');
 
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(function () { setTimeout(drawImageToCanvas, 0) });
   };
 
   image.src = source;
 }
 
-function changeColorProfile() {
-  /* The test image contains the Munsell colors in a known color space. Convert
-   * the colors to sRGB color space and test the transformed color accuracy.
-   */
-  window.testRunner.setColorProfile('sRGB', profileChanged);
-}
-
-function profileChanged() {
-  setTimeout(drawImageToCanvas, 0);
-}
-
 function drawImageToCanvas() {
   var image = document.querySelector('img');
 
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html
index 4a847f0..750375b0 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html
+++ b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html
@@ -21,13 +21,9 @@
 <script>
 window.onload = function() {
   if (window.testRunner)
-    setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+    setTimeout(function() { testRunner.notifyDone() }, 0);
 };
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html
index 61ec8896..8267d19 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html
+++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html
@@ -34,7 +34,7 @@
   video.addEventListener('seeked', function() {
     video.classList.add('filter');
     if (window.testRunner)
-      setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+      setTimeout(function() { testRunner.notifyDone() }, 0);
     video.pause();
   }, false);
 
@@ -46,10 +46,6 @@
   video.currentTime = time;
 }
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html
index 759d332..c32de0c 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html
+++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html
@@ -33,7 +33,7 @@
 
   video.addEventListener('seeked', function() {
     if (window.testRunner)
-      setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+      setTimeout(function() { testRunner.notifyDone() }, 0);
     video.pause();
   }, false);
 
@@ -45,10 +45,6 @@
   video.currentTime = time;
 }
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html
index 76e3f44..f22af0e 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html
+++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html
@@ -28,7 +28,7 @@
 
   video.addEventListener('seeked', function() {
     if (window.testRunner)
-      setTimeout(function() { testRunner.setColorProfile('colorSpin', done) }, 100);
+      setTimeout(function() { testRunner.notifyDone() }, 0);
     video.pause();
   }, false);
 
@@ -40,10 +40,6 @@
   video.currentTime = time;
 }
 
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
-
 if (window.testRunner) {
   testRunner.dumpAsTextWithPixelResults();
   testRunner.waitUntilDone();
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video.html b/third_party/WebKit/LayoutTests/media/color-profile-video.html
index 73172bdd..d3d1ab2 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-video.html
+++ b/third_party/WebKit/LayoutTests/media/color-profile-video.html
@@ -31,15 +31,11 @@
 window.onload = function() {
   if (window.testRunner) {
     document.querySelector('video').oncanplaythrough = function() {
-      testRunner.setColorProfile('colorSpin', done);
+      setTimeout(function() { testRunner.notifyDone() }, 0);
     };
   }
 
   setSrcByTagName('video', findMediaFile('video', 'content/test'));
 };
-
-function done() {
-  setTimeout(function() { testRunner.notifyDone() }, 0);
-}
 </script>
 </html>
diff --git a/third_party/WebKit/LayoutTests/media/resources/munsell-video-chart.js b/third_party/WebKit/LayoutTests/media/resources/munsell-video-chart.js
index 61c0dd2..ac9dfdd 100644
--- a/third_party/WebKit/LayoutTests/media/resources/munsell-video-chart.js
+++ b/third_party/WebKit/LayoutTests/media/resources/munsell-video-chart.js
@@ -5,23 +5,12 @@
 
   var image = document.querySelector('img');
   image.onload = function() {
-    runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged);
+    runAfterLayoutAndPaint(function () { setTimeout(drawImageToCanvas, 0) });
   };
 
   image.src = source;
 }
 
-function changeColorProfile() {
-  /* The test image contains the Munsell colors in a known color space. Convert
-   * the colors to sRGB color space and test the transformed color accuracy.
-   */
-  window.testRunner.setColorProfile('sRGB', profileChanged);
-}
-
-function profileChanged() {
-  setTimeout(drawImageToCanvas, 0);
-}
-
 function drawImageToCanvas() {
   var image = document.querySelector('img');
 
diff --git a/third_party/WebKit/Source/core/animation/AnimationInputHelpers.cpp b/third_party/WebKit/Source/core/animation/AnimationInputHelpers.cpp
index 2871b80e..7206405 100644
--- a/third_party/WebKit/Source/core/animation/AnimationInputHelpers.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationInputHelpers.cpp
@@ -254,7 +254,8 @@
     exception_state.ThrowTypeError("Easing may not be set to a list of values");
     return nullptr;
   }
-  return CSSToStyleMap::MapAnimationTimingFunction(value_list->Item(0), true);
+  return CSSToStyleMap::MapAnimationTimingFunction(value_list->Item(0), true,
+                                                   document);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/animation/TimingInputTest.cpp b/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
index 11faae80..f3f0ee7 100644
--- a/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
+++ b/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
@@ -15,11 +15,33 @@
 
 namespace blink {
 
-Timing ApplyTimingInputNumber(v8::Isolate* isolate,
-                              String timing_property,
-                              double timing_property_value,
-                              bool& timing_conversion_success,
-                              bool is_keyframeeffectoptions = true) {
+class AnimationTimingInputTest : public testing::Test {
+ public:
+  Timing ApplyTimingInputNumber(v8::Isolate*,
+                                String timing_property,
+                                double timing_property_value,
+                                bool& timing_conversion_success,
+                                bool is_keyframeeffectoptions = true);
+  Timing ApplyTimingInputString(v8::Isolate*,
+                                String timing_property,
+                                String timing_property_value,
+                                bool& timing_conversion_success,
+                                bool is_keyframeeffectoptions = true);
+
+ private:
+  void SetUp() override { page_holder_ = DummyPageHolder::Create(); }
+
+  Document* GetDocument() const { return &page_holder_->GetDocument(); }
+
+  std::unique_ptr<DummyPageHolder> page_holder_;
+};
+
+Timing AnimationTimingInputTest::ApplyTimingInputNumber(
+    v8::Isolate* isolate,
+    String timing_property,
+    double timing_property_value,
+    bool& timing_conversion_success,
+    bool is_keyframeeffectoptions) {
   v8::Local<v8::Object> timing_input = v8::Object::New(isolate);
   SetV8ObjectPropertyAsNumber(isolate, timing_input, timing_property,
                               timing_property_value);
@@ -30,7 +52,7 @@
     V8KeyframeEffectOptions::toImpl(isolate, timing_input,
                                     timing_input_dictionary, exception_state);
     timing_conversion_success =
-        TimingInput::Convert(timing_input_dictionary, result, nullptr,
+        TimingInput::Convert(timing_input_dictionary, result, GetDocument(),
                              exception_state) &&
         !exception_state.HadException();
   } else {
@@ -38,18 +60,19 @@
     V8KeyframeAnimationOptions::toImpl(
         isolate, timing_input, timing_input_dictionary, exception_state);
     timing_conversion_success =
-        TimingInput::Convert(timing_input_dictionary, result, nullptr,
+        TimingInput::Convert(timing_input_dictionary, result, GetDocument(),
                              exception_state) &&
         !exception_state.HadException();
   }
   return result;
 }
 
-Timing ApplyTimingInputString(v8::Isolate* isolate,
-                              String timing_property,
-                              String timing_property_value,
-                              bool& timing_conversion_success,
-                              bool is_keyframeeffectoptions = true) {
+Timing AnimationTimingInputTest::ApplyTimingInputString(
+    v8::Isolate* isolate,
+    String timing_property,
+    String timing_property_value,
+    bool& timing_conversion_success,
+    bool is_keyframeeffectoptions) {
   v8::Local<v8::Object> timing_input = v8::Object::New(isolate);
   SetV8ObjectPropertyAsString(isolate, timing_input, timing_property,
                               timing_property_value);
@@ -61,7 +84,7 @@
     V8KeyframeEffectOptions::toImpl(isolate, timing_input,
                                     timing_input_dictionary, exception_state);
     timing_conversion_success =
-        TimingInput::Convert(timing_input_dictionary, result, nullptr,
+        TimingInput::Convert(timing_input_dictionary, result, GetDocument(),
                              exception_state) &&
         !exception_state.HadException();
   } else {
@@ -69,14 +92,14 @@
     V8KeyframeAnimationOptions::toImpl(
         isolate, timing_input, timing_input_dictionary, exception_state);
     timing_conversion_success =
-        TimingInput::Convert(timing_input_dictionary, result, nullptr,
+        TimingInput::Convert(timing_input_dictionary, result, GetDocument(),
                              exception_state) &&
         !exception_state.HadException();
   }
   return result;
 }
 
-TEST(AnimationTimingInputTest, TimingInputStartDelay) {
+TEST_F(AnimationTimingInputTest, TimingInputStartDelay) {
   V8TestingScope scope;
   bool ignored_success;
   EXPECT_EQ(1.1, ApplyTimingInputNumber(scope.GetIsolate(), "delay", 1100,
@@ -105,7 +128,8 @@
                    .start_delay);
 }
 
-TEST(AnimationTimingInputTest, TimingInputStartDelayKeyframeAnimationOptions) {
+TEST_F(AnimationTimingInputTest,
+       TimingInputStartDelayKeyframeAnimationOptions) {
   V8TestingScope scope;
   bool ignored_success;
   EXPECT_EQ(1.1, ApplyTimingInputNumber(scope.GetIsolate(), "delay", 1100,
@@ -134,7 +158,7 @@
                    .start_delay);
 }
 
-TEST(AnimationTimingInputTest, TimingInputEndDelay) {
+TEST_F(AnimationTimingInputTest, TimingInputEndDelay) {
   V8TestingScope scope;
   bool ignored_success;
   EXPECT_EQ(10, ApplyTimingInputNumber(scope.GetIsolate(), "endDelay", 10000,
@@ -145,7 +169,7 @@
                       .end_delay);
 }
 
-TEST(AnimationTimingInputTest, TimingInputFillMode) {
+TEST_F(AnimationTimingInputTest, TimingInputFillMode) {
   V8TestingScope scope;
   Timing::FillMode default_fill_mode = Timing::FillMode::AUTO;
   bool ignored_success;
@@ -184,7 +208,7 @@
           .fill_mode);
 }
 
-TEST(AnimationTimingInputTest, TimingInputIterationStart) {
+TEST_F(AnimationTimingInputTest, TimingInputIterationStart) {
   V8TestingScope scope;
   bool success;
   EXPECT_EQ(1.1, ApplyTimingInputNumber(scope.GetIsolate(), "iterationStart",
@@ -211,7 +235,7 @@
   EXPECT_FALSE(success);
 }
 
-TEST(AnimationTimingInputTest, TimingInputIterationCount) {
+TEST_F(AnimationTimingInputTest, TimingInputIterationCount) {
   V8TestingScope scope;
   bool success;
   EXPECT_EQ(2.1, ApplyTimingInputNumber(scope.GetIsolate(), "iterations", 2.1,
@@ -239,7 +263,7 @@
   EXPECT_FALSE(success);
 }
 
-TEST(AnimationTimingInputTest, TimingInputIterationDuration) {
+TEST_F(AnimationTimingInputTest, TimingInputIterationDuration) {
   V8TestingScope scope;
   bool success;
   EXPECT_EQ(
@@ -275,7 +299,7 @@
   EXPECT_FALSE(success);
 }
 
-TEST(AnimationTimingInputTest, TimingInputDirection) {
+TEST_F(AnimationTimingInputTest, TimingInputDirection) {
   V8TestingScope scope;
   Timing::PlaybackDirection default_playback_direction =
       Timing::PlaybackDirection::NORMAL;
@@ -307,7 +331,7 @@
                 .direction);
 }
 
-TEST(AnimationTimingInputTest, TimingInputTimingFunction) {
+TEST_F(AnimationTimingInputTest, TimingInputTimingFunction) {
   V8TestingScope scope;
   const RefPtr<TimingFunction> default_timing_function =
       LinearTimingFunction::Shared();
@@ -409,7 +433,7 @@
   EXPECT_FALSE(success);
 }
 
-TEST(AnimationTimingInputTest, TimingInputEmpty) {
+TEST_F(AnimationTimingInputTest, TimingInputEmpty) {
   DummyExceptionStateForTesting exception_state;
   Timing control_timing;
   Timing updated_timing;
@@ -428,7 +452,7 @@
   EXPECT_EQ(*control_timing.timing_function, *updated_timing.timing_function);
 }
 
-TEST(AnimationTimingInputTest, TimingInputEmptyKeyframeAnimationOptions) {
+TEST_F(AnimationTimingInputTest, TimingInputEmptyKeyframeAnimationOptions) {
   DummyExceptionStateForTesting exception_state;
   Timing control_timing;
   Timing updated_timing;
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index cd022b7..6e69f48 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -1906,10 +1906,14 @@
   // this serialization.
   CSSValue* ligatures_value = ValueForFontVariantLigatures(style);
   CSSValue* numeric_value = ValueForFontVariantNumeric(style);
-  if (!DataEquivalent<CSSValue>(ligatures_value,
-                                CSSIdentifierValue::Create(CSSValueNormal)) ||
-      !DataEquivalent<CSSValue>(numeric_value,
-                                CSSIdentifierValue::Create(CSSValueNormal)))
+  // FIXME: Use DataEquivalent<CSSValue>(...) once http://crbug.com/729447 is
+  // resolved.
+  if (!DataEquivalent(
+          ligatures_value,
+          static_cast<CSSValue*>(CSSIdentifierValue::Create(CSSValueNormal))) ||
+      !DataEquivalent(
+          numeric_value,
+          static_cast<CSSValue*>(CSSIdentifierValue::Create(CSSValueNormal))))
     return nullptr;
 
   CSSIdentifierValue* caps_value = ValueForFontVariantCaps(style);
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
index 5687d25..5b80e0ca 100644
--- a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
@@ -40,6 +40,7 @@
 #include "core/css/CSSValuePair.h"
 #include "core/css/resolver/StyleBuilderConverter.h"
 #include "core/css/resolver/StyleResolverState.h"
+#include "core/frame/Deprecation.h"
 #include "core/style/BorderImageLengthBox.h"
 #include "core/style/FillLayer.h"
 
@@ -408,7 +409,8 @@
 
 PassRefPtr<TimingFunction> CSSToStyleMap::MapAnimationTimingFunction(
     const CSSValue& value,
-    bool allow_step_middle) {
+    bool allow_step_middle,
+    Document* document) {
   // FIXME: We should probably only call into this function with a valid
   // single timing function value which isn't initial or inherit. We can
   // currently get into here with initial since the parser expands unset
@@ -435,9 +437,15 @@
         return StepsTimingFunction::Preset(
             StepsTimingFunction::StepPosition::START);
       case CSSValueStepMiddle:
-        if (allow_step_middle)
+        if (allow_step_middle) {
+          DCHECK(document);
+          if (document) {
+            Deprecation::CountDeprecation(
+                *document, WebFeature::kDeprecatedTimingFunctionStepMiddle);
+          }
           return StepsTimingFunction::Preset(
               StepsTimingFunction::StepPosition::MIDDLE);
+        }
         return CSSTimingData::InitialTimingFunction();
       case CSSValueStepEnd:
         return StepsTimingFunction::Preset(
@@ -469,9 +477,16 @@
   const CSSStepsTimingFunctionValue& steps_timing_function =
       ToCSSStepsTimingFunctionValue(value);
   if (steps_timing_function.GetStepPosition() ==
-          StepsTimingFunction::StepPosition::MIDDLE &&
-      !allow_step_middle)
-    return CSSTimingData::InitialTimingFunction();
+      StepsTimingFunction::StepPosition::MIDDLE) {
+    if (!allow_step_middle) {
+      return CSSTimingData::InitialTimingFunction();
+    }
+    DCHECK(document);
+    if (document) {
+      Deprecation::CountDeprecation(
+          *document, WebFeature::kDeprecatedTimingFunctionStepMiddle);
+    }
+  }
   return StepsTimingFunction::Create(steps_timing_function.NumberOfSteps(),
                                      steps_timing_function.GetStepPosition());
 }
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.h b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.h
index 27b10ae..cfa9f969 100644
--- a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.h
+++ b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.h
@@ -32,6 +32,7 @@
 
 namespace blink {
 
+class Document;
 class FillLayer;
 class CSSValue;
 class StyleResolverState;
@@ -76,9 +77,13 @@
   static EAnimPlayState MapAnimationPlayState(const CSSValue&);
   static CSSTransitionData::TransitionProperty MapAnimationProperty(
       const CSSValue&);
+
+  // Pass a Document* if allow_step_middle is true so that the usage can be
+  // counted.
   static PassRefPtr<TimingFunction> MapAnimationTimingFunction(
       const CSSValue&,
-      bool allow_step_middle = false);
+      bool allow_step_middle = false,
+      Document* = nullptr);
 
   static void MapNinePieceImage(StyleResolverState&,
                                 CSSPropertyID,
diff --git a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
index 7861704..c1b0175 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
@@ -1519,6 +1519,133 @@
   EXPECT_STREQ("Abc 1", input->value().Utf8().data());
 }
 
+static String GetMarkedText(
+    DocumentMarkerController& document_marker_controller,
+    Node* node,
+    int marker_index) {
+  DocumentMarker* marker = document_marker_controller.Markers()[marker_index];
+  return node->textContent().Substring(
+      marker->StartOffset(), marker->EndOffset() - marker->StartOffset());
+}
+
+TEST_F(InputMethodControllerTest,
+       Marker_WhitespaceFixupAroundContentIndependentMarkerNotContainingSpace) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text blah</div>", "sample");
+
+  // Add marker under "text" (use TextMatch since Composition markers don't
+  // persist across editing operations)
+  EphemeralRange marker_range = PlainTextRange(8, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+  // Delete "Initial"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Delete "blah"
+  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Check that the marker is still attached to "text" and doesn't include
+  // either space around it
+  EXPECT_EQ(1u, GetDocument().Markers().MarkersFor(div->firstChild()).size());
+  EXPECT_STREQ("text",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
+TEST_F(InputMethodControllerTest,
+       Marker_WhitespaceFixupAroundContentIndependentMarkerBeginningWithSpace) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text blah</div>", "sample");
+
+  // Add marker under " text" (use TextMatch since Composition markers don't
+  // persist across editing operations)
+  EphemeralRange marker_range = PlainTextRange(7, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+  // Delete "Initial"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Delete "blah"
+  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Check that the marker is still attached to " text" and includes the space
+  // before "text" but not the space after
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("\xC2\xA0text",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
+TEST_F(InputMethodControllerTest,
+       Marker_WhitespaceFixupAroundContentIndependentMarkerEndingWithSpace) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text blah</div>", "sample");
+
+  // Add marker under "text " (use TextMatch since Composition markers don't
+  // persist across editing operations)
+  EphemeralRange marker_range = PlainTextRange(8, 13).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+  // Delete "Initial"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Delete "blah"
+  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Check that the marker is still attached to "text " and includes the space
+  // after "text" but not the space before
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("text\xC2\xA0",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
+TEST_F(
+    InputMethodControllerTest,
+    Marker_WhitespaceFixupAroundContentIndependentMarkerBeginningAndEndingWithSpaces) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text blah</div>", "sample");
+
+  // Add marker under " text " (use TextMatch since Composition markers don't
+  // persist across editing operations)
+  EphemeralRange marker_range = PlainTextRange(7, 13).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Delete "Initial"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Delete "blah"
+  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Check that the marker is still attached to " text " and includes both the
+  // space before "text" and the space after
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("\xC2\xA0text\xC2\xA0",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_ReplaceStartOfMarker) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>Initial text</div>", "sample");
@@ -1538,6 +1665,30 @@
 }
 
 TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceStartOfMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text</div>", "sample");
+
+  // Add marker under "Initial text"
+  EphemeralRange marker_range = PlainTextRange(0, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Replace "Initial" with "Original"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
+  Controller().CommitText(String("Original"), empty_underlines, 0);
+
+  // Verify marker is under "Original text"
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("Original text",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
+TEST_F(InputMethodControllerTest,
        ContentDependentMarker_ReplaceTextContainsStartOfMarker) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>This is some initial text</div>",
@@ -1557,6 +1708,31 @@
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceTextContainsStartOfMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>This is some initial text</div>",
+      "sample");
+
+  // Add marker under "initial text"
+  EphemeralRange marker_range = PlainTextRange(13, 25).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Replace "some initial" with "boring"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 8, 20);
+  Controller().CommitText(String("boring"), empty_underlines, 0);
+
+  // Verify marker is under " text"
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  EXPECT_STREQ(" text",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_ReplaceEndOfMarker) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>Initial text</div>", "sample");
@@ -1575,6 +1751,29 @@
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
+TEST_F(InputMethodControllerTest, ContentIndependentMarker_ReplaceEndOfMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text</div>", "sample");
+
+  // Add marker under "Initial text"
+  EphemeralRange marker_range = PlainTextRange(0, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Replace "text" with "string"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
+  Controller().CommitText(String("string"), empty_underlines, 0);
+
+  // Verify marker is under "Initial string"
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("Initial string",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
 TEST_F(InputMethodControllerTest,
        ContentDependentMarker_ReplaceTextContainsEndOfMarker) {
   Element* div = InsertHTMLElement(
@@ -1597,6 +1796,33 @@
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceTextContainsEndOfMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>This is some initial text</div>",
+      "sample");
+
+  // Add marker under "some initial"
+  EphemeralRange marker_range = PlainTextRange(8, 20).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Replace "initial text" with "content"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 13, 25);
+  Controller().CommitText(String("content"), empty_underlines, 0);
+
+  EXPECT_STREQ("This is some content", div->innerHTML().Utf8().data());
+
+  // Verify marker is under "some "
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("some ",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_ReplaceEntireMarker) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>Initial text</div>", "sample");
@@ -1616,6 +1842,30 @@
 }
 
 TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceEntireMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text</div>", "sample");
+
+  // Add marker under "text"
+  EphemeralRange marker_range = PlainTextRange(8, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Replace "text" with "string"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
+  Controller().CommitText(String("string"), empty_underlines, 0);
+
+  // Verify marker is under "string"
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  ASSERT_STREQ("string",
+               GetMarkedText(GetDocument().Markers(), div->firstChild(), 0)
+                   .Utf8()
+                   .data());
+}
+
+TEST_F(InputMethodControllerTest,
        ContentDependentMarker_ReplaceTextWithMarkerAtBeginning) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>Initial text</div>", "sample");
@@ -1637,6 +1887,28 @@
 }
 
 TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtBeginning) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text</div>", "sample");
+
+  // Add marker under "Initial"
+  EphemeralRange marker_range = PlainTextRange(0, 7).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Replace "Initial text" with "New string"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
+  Controller().CommitText(String("New string"), empty_underlines, 0);
+
+  // Verify marker was removed
+  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
+}
+
+TEST_F(InputMethodControllerTest,
        ContentDependentMarker_ReplaceTextWithMarkerAtEnd) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>Initial text</div>", "sample");
@@ -1657,6 +1929,28 @@
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_ReplaceTextWithMarkerAtEnd) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>Initial text</div>", "sample");
+
+  // Add marker under "text"
+  EphemeralRange marker_range = PlainTextRange(8, 12).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Replace "Initial text" with "New string"
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
+  Controller().CommitText(String("New string"), empty_underlines, 0);
+
+  // Verify marker was removed
+  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_Deletions) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>1111122222333334444455555</div>",
@@ -1699,6 +1993,59 @@
   EXPECT_EQ(16u, GetDocument().Markers().Markers()[1]->EndOffset());
 }
 
+TEST_F(InputMethodControllerTest, ContentIndependentMarker_Deletions) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>1111122222333334444455555</div>",
+      "sample");
+
+  EphemeralRange marker_range = PlainTextRange(0, 5).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(5, 10).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(10, 15).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(15, 20).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(20, 25).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(5u, GetDocument().Markers().Markers().size());
+
+  // Delete third marker and portions of second and fourth
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 8, 17);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  // Verify markers were updated correctly
+  EXPECT_EQ(4u, GetDocument().Markers().Markers().size());
+
+  EXPECT_EQ(0u, GetDocument().Markers().Markers()[0]->StartOffset());
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[0]->EndOffset());
+
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[1]->StartOffset());
+  EXPECT_EQ(8u, GetDocument().Markers().Markers()[1]->EndOffset());
+
+  EXPECT_EQ(8u, GetDocument().Markers().Markers()[2]->StartOffset());
+  EXPECT_EQ(11u, GetDocument().Markers().Markers()[2]->EndOffset());
+
+  EXPECT_EQ(11u, GetDocument().Markers().Markers()[3]->StartOffset());
+  EXPECT_EQ(16u, GetDocument().Markers().Markers()[3]->EndOffset());
+}
+
 TEST_F(InputMethodControllerTest,
        ContentDependentMarker_DeleteExactlyOnMarker) {
   Element* div = InsertHTMLElement(
@@ -1718,6 +2065,26 @@
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_DeleteExactlyOnMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>1111122222333334444455555</div>",
+      "sample");
+
+  EphemeralRange marker_range = PlainTextRange(5, 10).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Delete exactly on the marker
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 5, 10);
+  Controller().CommitText(String(""), empty_underlines, 0);
+  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_DeleteMiddleOfMarker) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>1111122222333334444455555</div>",
@@ -1737,6 +2104,28 @@
 }
 
 TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_DeleteMiddleOfMarker) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>1111122222333334444455555</div>",
+      "sample");
+
+  EphemeralRange marker_range = PlainTextRange(5, 10).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  // Delete middle of marker
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetCompositionFromExistingText(empty_underlines, 6, 9);
+  Controller().CommitText(String(""), empty_underlines, 0);
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[0]->StartOffset());
+  EXPECT_EQ(7u, GetDocument().Markers().Markers()[0]->EndOffset());
+}
+
+TEST_F(InputMethodControllerTest,
        ContentDependentMarker_InsertInMarkerInterior) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>1111122222333334444455555</div>",
@@ -1770,6 +2159,46 @@
   EXPECT_EQ(20u, GetDocument().Markers().Markers()[1]->EndOffset());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_InsertInMarkerInterior) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>1111122222333334444455555</div>",
+      "sample");
+
+  EphemeralRange marker_range = PlainTextRange(0, 5).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(5, 10).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(10, 15).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
+
+  // insert in middle of second marker
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetComposition("", empty_underlines, 7, 7);
+  Controller().CommitText(String("66666"), empty_underlines, -7);
+
+  EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
+
+  EXPECT_EQ(0u, GetDocument().Markers().Markers()[0]->StartOffset());
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[0]->EndOffset());
+
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[1]->StartOffset());
+  EXPECT_EQ(15u, GetDocument().Markers().Markers()[1]->EndOffset());
+
+  EXPECT_EQ(15u, GetDocument().Markers().Markers()[2]->StartOffset());
+  EXPECT_EQ(20u, GetDocument().Markers().Markers()[2]->EndOffset());
+}
+
 TEST_F(InputMethodControllerTest, ContentDependentMarker_InsertBetweenMarkers) {
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>1111122222333334444455555</div>",
@@ -1805,6 +2234,45 @@
   EXPECT_EQ(25u, GetDocument().Markers().Markers()[2]->EndOffset());
 }
 
+TEST_F(InputMethodControllerTest,
+       ContentIndependentMarker_InsertBetweenMarkers) {
+  Element* div = InsertHTMLElement(
+      "<div id='sample' contenteditable>1111122222333334444455555</div>",
+      "sample");
+
+  EphemeralRange marker_range = PlainTextRange(0, 5).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(5, 15).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  marker_range = PlainTextRange(15, 20).CreateRange(*div);
+  GetDocument().Markers().AddActiveSuggestionMarker(
+      marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
+      Color::kBlack);
+
+  EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
+
+  Vector<CompositionUnderline> empty_underlines;
+  Controller().SetComposition("", empty_underlines, 5, 5);
+  Controller().CommitText(String("77777"), empty_underlines, 0);
+
+  EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
+
+  EXPECT_EQ(0u, GetDocument().Markers().Markers()[0]->StartOffset());
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[0]->EndOffset());
+
+  EXPECT_EQ(10u, GetDocument().Markers().Markers()[1]->StartOffset());
+  EXPECT_EQ(20u, GetDocument().Markers().Markers()[1]->EndOffset());
+
+  EXPECT_EQ(20u, GetDocument().Markers().Markers()[2]->StartOffset());
+  EXPECT_EQ(25u, GetDocument().Markers().Markers()[2]->EndOffset());
+}
+
 // For http://crbug.com/712761
 TEST_F(InputMethodControllerTest, TextInputTypeAtBeforeEditable) {
   GetDocument().body()->setContentEditable("true", ASSERT_NO_EXCEPTION);
diff --git a/third_party/WebKit/Source/core/frame/BUILD.gn b/third_party/WebKit/Source/core/frame/BUILD.gn
index cbc5f765..dcd6609b 100644
--- a/third_party/WebKit/Source/core/frame/BUILD.gn
+++ b/third_party/WebKit/Source/core/frame/BUILD.gn
@@ -49,6 +49,8 @@
     "FrameView.h",
     "FrameViewAutoSizeInfo.cpp",
     "FrameViewAutoSizeInfo.h",
+    "FullscreenController.cpp",
+    "FullscreenController.h",
     "History.cpp",
     "History.h",
     "HostsUsingFeatures.cpp",
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp
index ba6b0bc2..8a190930 100644
--- a/third_party/WebKit/Source/core/frame/Deprecation.cpp
+++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -440,6 +440,11 @@
           "the CredentialsContainer.preventSilentAccess method", M62,
           "4781762488041472");
 
+    case WebFeature::kDeprecatedTimingFunctionStepMiddle:
+      return replacedWillBeRemoved(
+          "The step timing function with step position 'middle'",
+          "the frames timing function", M62, "5189363944128512");
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
       return String();
diff --git a/third_party/WebKit/Source/web/FullscreenController.cpp b/third_party/WebKit/Source/core/frame/FullscreenController.cpp
similarity index 98%
rename from third_party/WebKit/Source/web/FullscreenController.cpp
rename to third_party/WebKit/Source/core/frame/FullscreenController.cpp
index 00d9521..faa3326 100644
--- a/third_party/WebKit/Source/web/FullscreenController.cpp
+++ b/third_party/WebKit/Source/core/frame/FullscreenController.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "web/FullscreenController.h"
+#include "core/frame/FullscreenController.h"
 
 #include "core/dom/Document.h"
 #include "core/dom/Fullscreen.h"
@@ -258,9 +258,10 @@
     return;
 
   web_view_base_->SetPageScaleFactor(initial_page_scale_factor_);
-  if (web_view_base_->MainFrame()->IsWebLocalFrame())
+  if (web_view_base_->MainFrame()->IsWebLocalFrame()) {
     web_view_base_->MainFrame()->SetScrollOffset(
         WebSize(initial_scroll_offset_));
+  }
   web_view_base_->SetVisualViewportOffset(initial_visual_viewport_offset_);
   // Background color override was already restored when
   // fullscreenElementChanged([..], nullptr) was called while exiting.
diff --git a/third_party/WebKit/Source/web/FullscreenController.h b/third_party/WebKit/Source/core/frame/FullscreenController.h
similarity index 97%
rename from third_party/WebKit/Source/web/FullscreenController.h
rename to third_party/WebKit/Source/core/frame/FullscreenController.h
index f8d49390..2de17f99 100644
--- a/third_party/WebKit/Source/web/FullscreenController.h
+++ b/third_party/WebKit/Source/core/frame/FullscreenController.h
@@ -33,6 +33,7 @@
 
 #include <memory>
 
+#include "core/CoreExport.h"
 #include "platform/geometry/FloatPoint.h"
 #include "platform/geometry/IntSize.h"
 #include "platform/graphics/Color.h"
@@ -43,7 +44,7 @@
 class LocalFrame;
 class WebViewBase;
 
-class FullscreenController {
+class CORE_EXPORT FullscreenController {
  public:
   static std::unique_ptr<FullscreenController> Create(WebViewBase*);
 
diff --git a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
index 229ebf7..79505c1 100644
--- a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
+++ b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
@@ -73,12 +73,11 @@
 ScriptPromise BeforeInstallPromptEvent::prompt(ScriptState* script_state) {
   // |m_bannerService| must be bound to allow us to inform the AppBannerService
   // to display the banner now.
-  if (!defaultPrevented() || prompt_called_ || !banner_service_.is_bound()) {
+  if (prompt_called_ || !banner_service_.is_bound()) {
     return ScriptPromise::RejectWithDOMException(
         script_state,
         DOMException::Create(kInvalidStateError,
-                             "The prompt() method may only be called once, "
-                             "following preventDefault()."));
+                             "The prompt() method may only be called once."));
   }
 
   UseCounter::Count(ExecutionContext::From(script_state),
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index d965fb52..45dc5cf 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -49,8 +49,6 @@
     "ExternalDateTimeChooser.h",
     "ExternalPopupMenu.cpp",
     "ExternalPopupMenu.h",
-    "FullscreenController.cpp",
-    "FullscreenController.h",
     "InspectorOverlayAgent.cpp",
     "InspectorOverlayAgent.h",
     "LinkHighlightImpl.cpp",
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 9c44ea0..aba6c566 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -58,6 +58,7 @@
 #include "core/exported/WebSettingsImpl.h"
 #include "core/frame/BrowserControls.h"
 #include "core/frame/EventHandlerRegistry.h"
+#include "core/frame/FullscreenController.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/LocalFrameClient.h"
 #include "core/frame/LocalFrameView.h"
@@ -166,7 +167,6 @@
 #include "public/web/WebViewClient.h"
 #include "public/web/WebWindowFeatures.h"
 #include "web/DedicatedWorkerMessagingProxyProviderImpl.h"
-#include "web/FullscreenController.h"
 #include "web/LinkHighlightImpl.h"
 #include "web/PageOverlay.h"
 #include "web/PrerendererClientImpl.h"
diff --git a/third_party/WebKit/public/platform/WebFeature.h b/third_party/WebKit/public/platform/WebFeature.h
index a251382..f5b42e1 100644
--- a/third_party/WebKit/public/platform/WebFeature.h
+++ b/third_party/WebKit/public/platform/WebFeature.h
@@ -1558,6 +1558,7 @@
   kBudgetAPIGetCost = 2021,
   kBudgetAPIGetBudget = 2022,
   kCrossOriginMainFrameNulledNonEmptyNameAccessed = 2023,
+  kDeprecatedTimingFunctionStepMiddle = 2024,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4f057e4..0c4a5dc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -15450,6 +15450,7 @@
   <int value="2021" label="BudgetAPIGetCost"/>
   <int value="2022" label="BudgetAPIGetBudget"/>
   <int value="2023" label="CrossOriginMainFrameNulledNonEmptyNameAccessed"/>
+  <int value="2024" label="DeprecatedTimingFunctionStepMiddle"/>
 </enum>
 
 <enum name="FeedbackSource" type="int">
diff --git a/ui/accessibility/ax_enums.idl b/ui/accessibility/ax_enums.idl
index 3eb77fd..f5b0c40 100644
--- a/ui/accessibility/ax_enums.idl
+++ b/ui/accessibility/ax_enums.idl
@@ -347,7 +347,6 @@
     placeholder,
     role,
     role_description,
-    shortcut,
     url,
     value
   };
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index eea07f3..1655b39 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -814,9 +814,6 @@
       case AX_ATTR_ROLE_DESCRIPTION:
         result += " role_description=" + value;
         break;
-      case AX_ATTR_SHORTCUT:
-        result += " shortcut=" + value;
-        break;
       case AX_ATTR_URL:
         result += " url=" + value;
         break;
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 7d385a6..5c5e16a 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -462,7 +462,7 @@
   AXPlatformNodeWin* target;
   COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, acc_key, target);
 
-  return target->GetStringAttributeAsBstr(ui::AX_ATTR_SHORTCUT, acc_key);
+  return target->GetStringAttributeAsBstr(ui::AX_ATTR_KEY_SHORTCUTS, acc_key);
 }
 
 STDMETHODIMP AXPlatformNodeWin::get_accName(
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index ee3b81f..506267c 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -178,7 +178,7 @@
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleShortcut) {
   AXNodeData root;
   root.id = 1;
-  root.AddStringAttribute(AX_ATTR_SHORTCUT, "Shortcut");
+  root.AddStringAttribute(AX_ATTR_KEY_SHORTCUTS, "Shortcut");
   Init(root);
 
   ScopedComPtr<IAccessible> root_obj(GetRootIAccessible());