diff --git a/DEPS b/DEPS
index a8e176c..4621d3d 100644
--- a/DEPS
+++ b/DEPS
@@ -92,7 +92,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'c56d8851ea987023cc73981a70d261b3f6427545',
+  'freetype_revision': 'cf8d9b4ce3fa2c6cd9ccb25585bc17a355c987b0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 7f700f23..7d9b656 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -356,8 +356,12 @@
 void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
                                          const MemoryAllocatorDumpGuid& target,
                                          int importance) {
-  DCHECK(allocator_dumps_edges_.count(source) == 0 ||
-         allocator_dumps_edges_[source].overridable);
+  // This will either override an existing edge or create a new one.
+  auto it = allocator_dumps_edges_.find(source);
+  if (it != allocator_dumps_edges_.end()) {
+    DCHECK_EQ(target.ToUint64(),
+              allocator_dumps_edges_[source].target.ToUint64());
+  }
   allocator_dumps_edges_[source] = {
       source, target, importance, kEdgeTypeOwnership, false /* overridable */};
 }
@@ -426,8 +430,9 @@
 
     // Create an edge between local dump of the client and the local dump of the
     // SharedMemoryTracker. Do not need to create the dumps here since the
-    // tracker would create them.
-    AddOwnershipEdge(client_local_dump_guid, local_shm_guid);
+    // tracker would create them. The importance is also required here for the
+    // case of single process mode.
+    AddOwnershipEdge(client_local_dump_guid, local_shm_guid, importance);
 
     // TODO(ssid): Handle the case of weak dumps here. This needs a new function
     // GetOrCreaetGlobalDump() in PMD since we need to change the behavior of
diff --git a/base/trace_event/process_memory_dump_unittest.cc b/base/trace_event/process_memory_dump_unittest.cc
index 1c1f3bf..47eb10ce 100644
--- a/base/trace_event/process_memory_dump_unittest.cc
+++ b/base/trace_event/process_memory_dump_unittest.cc
@@ -359,7 +359,7 @@
   EXPECT_EQ(1, edges.find(shm_local_guid2)->second.importance);
   EXPECT_FALSE(edges.find(shm_local_guid2)->second.overridable);
   EXPECT_EQ(shm_local_guid2, edges.find(client_dump2->guid())->second.target);
-  EXPECT_EQ(0, edges.find(client_dump2->guid())->second.importance);
+  EXPECT_EQ(1, edges.find(client_dump2->guid())->second.importance);
   EXPECT_FALSE(edges.find(client_dump2->guid())->second.overridable);
 }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index a943252..3addc59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -161,7 +161,6 @@
             "ContentSuggestionsFaviconsFromNewServer";
     public static final String CONTENT_SUGGESTIONS_NOTIFICATIONS =
             "ContentSuggestionsNotifications";
-    public static final String CONTENT_SUGGESTIONS_CATEGORIES = "ContentSuggestionsCategories";
     public static final String CONTENT_SUGGESTIONS_LARGE_THUMBNAIL =
             "ContentSuggestionsLargeThumbnail";
     public static final String CONTENT_SUGGESTIONS_SETTINGS = "ContentSuggestionsSettings";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
index 654beba..f3d2a81 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -4,45 +4,20 @@
 
 package org.chromium.chrome.browser.ntp.snippets;
 
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.media.ThumbnailUtils;
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.support.annotation.Nullable;
-import android.support.v4.text.BidiFormatter;
 import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Callback;
-import org.chromium.base.Promise;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
-import org.chromium.chrome.browser.download.DownloadUtils;
-import org.chromium.chrome.browser.download.ui.DownloadFilter;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.ContextMenuManager.ContextMenuItemId;
 import org.chromium.chrome.browser.ntp.cards.CardViewHolder;
 import org.chromium.chrome.browser.ntp.cards.ImpressionTracker;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo;
-import org.chromium.chrome.browser.suggestions.ImageFetcher;
+import org.chromium.chrome.browser.suggestions.SuggestionsBinder;
 import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.widget.TintedImageView;
 import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserver;
 import org.chromium.chrome.browser.widget.displaystyle.DisplayStyleObserverAdapter;
 import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
@@ -50,7 +25,6 @@
 import org.chromium.chrome.browser.widget.displaystyle.VerticalDisplayStyle;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
-import java.util.concurrent.TimeUnit;
 /**
  * A class that represents the view for a single card snippet.
  */
@@ -62,34 +36,12 @@
     public static final RefreshOfflineBadgeVisibilityCallback
             REFRESH_OFFLINE_BADGE_VISIBILITY_CALLBACK = new RefreshOfflineBadgeVisibilityCallback();
 
-    private static final String ARTICLE_AGE_FORMAT_STRING = " - %s";
-    private static final int FADE_IN_ANIMATION_TIME_MS = 300;
-
     private final SuggestionsUiDelegate mUiDelegate;
     private final UiConfig mUiConfig;
-    private final ImageFetcher mImageFetcher;
+    private final SuggestionsBinder mSuggestionsBinder;
 
-    private final LinearLayout mTextLayout;
-    private final TextView mHeadlineTextView;
-    private final TextView mPublisherTextView;
-    private final TextView mArticleSnippetTextView;
-    private final TextView mArticleAgeTextView;
-    private final TintedImageView mThumbnailView;
-    private final ImageView mThumbnailVideoOverlay;
-    private final ImageView mOfflineBadge;
-    private final View mPublisherBar;
-
-    private final int mThumbnailSize;
-    /** Total horizontal space occupied by the thumbnail, sum of its size and margin. */
-    private final int mThumbnailFootprintPx;
-    private final int mIconBackgroundColor;
-    private final ColorStateList mIconForegroundColorList;
-
-    @Nullable
-    private ImageFetcher.DownloadThumbnailRequest mThumbnailRequest;
-    private SnippetArticle mArticle;
     private SuggestionsCategoryInfo mCategoryInfo;
-    private int mPublisherFaviconSizePx;
+    private SnippetArticle mArticle;
 
     /**
      * Constructs a {@link SnippetArticleViewHolder} item used to display snippets.
@@ -108,29 +60,7 @@
 
         mUiDelegate = uiDelegate;
         mUiConfig = uiConfig;
-        mImageFetcher = mUiDelegate.getImageFetcher();
-
-        mTextLayout = (LinearLayout) itemView.findViewById(R.id.text_layout);
-        mHeadlineTextView = (TextView) itemView.findViewById(R.id.article_headline);
-        mPublisherTextView = (TextView) itemView.findViewById(R.id.article_publisher);
-        mArticleSnippetTextView = (TextView) itemView.findViewById(R.id.article_snippet);
-        mArticleAgeTextView = (TextView) itemView.findViewById(R.id.article_age);
-        mThumbnailView = (TintedImageView) itemView.findViewById(R.id.article_thumbnail);
-        mThumbnailVideoOverlay =
-                (ImageView) itemView.findViewById(R.id.article_thumbnail_video_overlay);
-        mPublisherBar = itemView.findViewById(R.id.publisher_bar);
-        mOfflineBadge = (ImageView) itemView.findViewById(R.id.offline_icon);
-
-        boolean useLargeThumbnailLayout =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL);
-        mThumbnailSize = itemView.getResources().getDimensionPixelSize(useLargeThumbnailLayout
-                        ? R.dimen.snippets_thumbnail_size_large
-                        : R.dimen.snippets_thumbnail_size);
-        mThumbnailFootprintPx = mThumbnailSize
-                + itemView.getResources().getDimensionPixelSize(R.dimen.snippets_thumbnail_margin);
-
-        mIconBackgroundColor = DownloadUtils.getIconBackgroundColor(parent.getContext());
-        mIconForegroundColorList = DownloadUtils.getIconForegroundColorList(parent.getContext());
+        mSuggestionsBinder = new SuggestionsBinder(itemView, uiDelegate);
 
         new ImpressionTracker(itemView, this);
         new DisplayStyleObserverAdapter(itemView, uiConfig, new DisplayStyleObserver() {
@@ -194,33 +124,11 @@
 
         mArticle = article;
         mCategoryInfo = categoryInfo;
+
         updateLayout();
 
-        mHeadlineTextView.setText(mArticle.mTitle);
+        mSuggestionsBinder.updateViewInformation(mArticle);
 
-        // The favicon of the publisher should match the TextView height.
-        int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        mPublisherTextView.measure(widthSpec, heightSpec);
-        mPublisherFaviconSizePx = mPublisherTextView.getMeasuredHeight();
-
-        mArticleSnippetTextView.setText(mArticle.mPreviewText);
-        mPublisherTextView.setText(getPublisherString(mArticle));
-        mArticleAgeTextView.setText(getArticleAge(mArticle));
-        setThumbnail();
-
-        // Set the favicon of the publisher.
-        // We start initialising with the default favicon to reserve the space and prevent the text
-        // from moving later.
-        setDefaultFaviconOnView();
-        mImageFetcher.makeFaviconRequest(mArticle, mPublisherFaviconSizePx, new Callback<Bitmap>() {
-            @Override
-            public void onResult(Bitmap image) {
-                setFaviconOnView(image);
-            }
-        });
-
-        mOfflineBadge.setVisibility(View.GONE);
         refreshOfflineBadgeVisibility();
     }
 
@@ -237,37 +145,9 @@
         boolean showThumbnail = shouldShowThumbnail(layout);
         boolean showThumbnailVideoOverlay = shouldShowThumbnailVideoOverlay(showThumbnail);
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)) {
-            mTextLayout.setMinimumHeight(showThumbnail ? mThumbnailSize : 0);
-        }
-        mHeadlineTextView.setVisibility(showHeadline ? View.VISIBLE : View.GONE);
-        mHeadlineTextView.setMaxLines(getHeaderMaxLines(horizontalStyle, verticalStyle, layout));
-        mArticleSnippetTextView.setVisibility(showDescription ? View.VISIBLE : View.GONE);
-        mThumbnailView.setVisibility(showThumbnail ? View.VISIBLE : View.GONE);
-        mThumbnailVideoOverlay.setVisibility(showThumbnailVideoOverlay ? View.VISIBLE : View.GONE);
-
-        ViewGroup.MarginLayoutParams publisherBarParams =
-                (ViewGroup.MarginLayoutParams) mPublisherBar.getLayoutParams();
-
-        if (showDescription) {
-            publisherBarParams.topMargin = mPublisherBar.getResources().getDimensionPixelSize(
-                    R.dimen.snippets_publisher_margin_top_with_article_snippet);
-        } else if (showHeadline) {
-            // When we show a headline and not a description, we reduce the top margin of the
-            // publisher bar.
-            publisherBarParams.topMargin = mPublisherBar.getResources().getDimensionPixelSize(
-                    R.dimen.snippets_publisher_margin_top_without_article_snippet);
-        } else {
-            // When there is no headline and no description, we remove the top margin of the
-            // publisher bar.
-            publisherBarParams.topMargin = 0;
-        }
-
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)) {
-            ApiCompatibilityUtils.setMarginEnd(
-                    publisherBarParams, showThumbnail ? mThumbnailFootprintPx : 0);
-        }
-        mPublisherBar.setLayoutParams(publisherBarParams);
+        mSuggestionsBinder.updateFieldsVisibility(showHeadline, showDescription, showThumbnail,
+                showThumbnailVideoOverlay,
+                getHeaderMaxLines(horizontalStyle, verticalStyle, layout));
     }
 
     /** If the title is empty (or contains only whitespace characters), we do not show it. */
@@ -311,199 +191,10 @@
         return shouldShowDescription(horizontalStyle, verticalStyle, layout) ? 2 : 3;
     }
 
-    private static String getPublisherString(SnippetArticle article) {
-        // We format the publisher here so that having a publisher name in an RTL language
-        // doesn't mess up the formatting on an LTR device and vice versa.
-        return BidiFormatter.getInstance().unicodeWrap(article.mPublisher);
-    }
-
-    private static String getArticleAge(SnippetArticle article) {
-        if (article.mPublishTimestampMilliseconds == 0) return "";
-
-        // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this
-        // has never been called before it loads the current time zone from disk. In most likelihood
-        // this will have been called previously and the current time zone will have been cached,
-        // but in some cases (eg instrumentation tests) it will cause a strict mode violation.
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        CharSequence relativeTimeSpan;
-        try {
-            long time = SystemClock.elapsedRealtime();
-            relativeTimeSpan =
-                    DateUtils.getRelativeTimeSpanString(article.mPublishTimestampMilliseconds,
-                            System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS);
-            RecordHistogram.recordTimesHistogram("Android.StrictMode.SnippetUIBuildTime",
-                    SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS);
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-
-        // We add a dash before the elapsed time, e.g. " - 14 minutes ago".
-        return String.format(ARTICLE_AGE_FORMAT_STRING,
-                BidiFormatter.getInstance().unicodeWrap(relativeTimeSpan));
-    }
-
-    private void setThumbnailFromBitmap(Bitmap thumbnail) {
-        assert thumbnail != null;
-        assert !thumbnail.isRecycled();
-        assert thumbnail.getWidth() <= mThumbnailSize || thumbnail.getHeight() <= mThumbnailSize;
-
-        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mThumbnailView.setBackground(null);
-        mThumbnailView.setImageBitmap(thumbnail);
-        mThumbnailView.setTint(null);
-    }
-
-    private void setThumbnailFromFileType(int fileType) {
-        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        mThumbnailView.setBackgroundColor(mIconBackgroundColor);
-        mThumbnailView.setImageResource(
-                DownloadUtils.getIconResId(fileType, DownloadUtils.ICON_SIZE_36_DP));
-        mThumbnailView.setTint(mIconForegroundColorList);
-    }
-
-    private void setDownloadThumbnail() {
-        assert mArticle.isDownload();
-        if (!mArticle.isAssetDownload()) {
-            setThumbnailFromFileType(DownloadFilter.FILTER_PAGE);
-            return;
-        }
-
-        int fileType = DownloadFilter.fromMimeType(mArticle.getAssetDownloadMimeType());
-        if (fileType == DownloadFilter.FILTER_IMAGE) {
-            // For image downloads, attempt to fetch a thumbnail.
-            ImageFetcher.DownloadThumbnailRequest thumbnailRequest =
-                    mImageFetcher.makeDownloadThumbnailRequest(mArticle, mThumbnailSize);
-
-            Promise<Bitmap> thumbnailReceivedPromise = thumbnailRequest.getPromise();
-
-            if (thumbnailReceivedPromise.isFulfilled()) {
-                // If the thumbnail was cached, then it will be retrieved synchronously, the promise
-                // will be fulfilled and we can set the thumbnail immediately.
-                setThumbnailFromBitmap(thumbnailReceivedPromise.getResult());
-                return;
-            }
-
-            mThumbnailRequest = thumbnailRequest;
-
-            // Queue a callback to be called after the thumbnail is retrieved asynchronously.
-            thumbnailReceivedPromise.then(new FetchImageCallback(mArticle, mThumbnailSize));
-        }
-
-        // Set a placeholder for the file type.
-        setThumbnailFromFileType(fileType);
-    }
-
-    private void setThumbnail() {
-        // If there's still a pending thumbnail fetch, cancel it.
-        cancelImageFetch();
-
-        // mThumbnailView's visibility is modified in updateLayout().
-        if (mThumbnailView.getVisibility() != View.VISIBLE) return;
-        Bitmap thumbnail = mArticle.getThumbnailBitmap();
-        if (thumbnail != null) {
-            setThumbnailFromBitmap(thumbnail);
-            return;
-        }
-
-        if (mArticle.isDownload()) {
-            setDownloadThumbnail();
-            return;
-        }
-
-        // Temporarily set placeholder and then fetch the thumbnail from a provider.
-        mThumbnailView.setBackground(null);
-        mThumbnailView.setImageResource(R.drawable.ic_snippet_thumbnail_placeholder);
-        mThumbnailView.setTint(null);
-        mImageFetcher.makeArticleThumbnailRequest(
-                mArticle, new FetchImageCallback(mArticle, mThumbnailSize));
-    }
-
     /** Updates the visibility of the card's offline badge by checking the bound article's info. */
     private void refreshOfflineBadgeVisibility() {
         boolean visible = mArticle.getOfflinePageOfflineId() != null || mArticle.isAssetDownload();
-        if (visible == (mOfflineBadge.getVisibility() == View.VISIBLE)) return;
-        mOfflineBadge.setVisibility(visible ? View.VISIBLE : View.GONE);
-    }
-
-    private void cancelImageFetch() {
-        if (mThumbnailRequest != null) {
-            mThumbnailRequest.cancel();
-            mThumbnailRequest = null;
-        }
-    }
-
-    private void fadeThumbnailIn(Bitmap thumbnail) {
-        assert mThumbnailView.getDrawable() != null;
-
-        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mThumbnailView.setBackground(null);
-        mThumbnailView.setTint(null);
-        int duration = (int) (FADE_IN_ANIMATION_TIME_MS
-                * ChromeAnimation.Animation.getAnimationMultiplier());
-        if (duration == 0) {
-            mThumbnailView.setImageBitmap(thumbnail);
-            return;
-        }
-
-        // Cross-fade between the placeholder and the thumbnail. We cross-fade because the incoming
-        // image may have transparency and we don't want the previous image showing up behind.
-        Drawable[] layers = {mThumbnailView.getDrawable(),
-                new BitmapDrawable(mThumbnailView.getResources(), thumbnail)};
-        TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
-        mThumbnailView.setImageDrawable(transitionDrawable);
-        transitionDrawable.setCrossFadeEnabled(true);
-        transitionDrawable.startTransition(duration);
-    }
-
-    private void setDefaultFaviconOnView() {
-        setFaviconOnView(ApiCompatibilityUtils.getDrawable(
-                mPublisherTextView.getContext().getResources(), R.drawable.default_favicon));
-    }
-
-    private void setFaviconOnView(Bitmap image) {
-        setFaviconOnView(new BitmapDrawable(mPublisherTextView.getContext().getResources(), image));
-    }
-
-    private void setFaviconOnView(Drawable drawable) {
-        drawable.setBounds(0, 0, mPublisherFaviconSizePx, mPublisherFaviconSizePx);
-        ApiCompatibilityUtils.setCompoundDrawablesRelative(
-                mPublisherTextView, drawable, null, null, null);
-        mPublisherTextView.setVisibility(View.VISIBLE);
-    }
-
-    private class FetchImageCallback extends Callback<Bitmap> {
-        private final SnippetArticle mSuggestion;
-        private final int mThumbnailSize;
-        private final boolean mIsBitmapOwned;
-
-        FetchImageCallback(SnippetArticle suggestion, int size) {
-            mSuggestion = suggestion;
-            mThumbnailSize = size;
-            mIsBitmapOwned = suggestion.isArticle();
-        }
-
-        @Override
-        public void onResult(Bitmap thumbnail) {
-            if (thumbnail == null) return; // Nothing to do, we keep the placeholder.
-
-            // We need to crop and scale the downloaded bitmap, as the ImageView we set it on won't
-            // be able to do so when using a TransitionDrawable (as opposed to the straight bitmap).
-            // That's a limitation of TransitionDrawable, which doesn't handle layers of varying
-            // sizes.
-            if (thumbnail.getHeight() != mThumbnailSize || thumbnail.getWidth() != mThumbnailSize) {
-                // Resize the thumbnail. If we fully own the input bitmap (e.g. it isn't cached
-                // anywhere else), recycle the input image in the process.
-                thumbnail = ThumbnailUtils.extractThumbnail(thumbnail, mThumbnailSize,
-                        mThumbnailSize, mIsBitmapOwned ? ThumbnailUtils.OPTIONS_RECYCLE_INPUT : 0);
-            }
-
-            // Store the bitmap to skip the download task next time we display this snippet.
-            mSuggestion.setThumbnailBitmap(mUiDelegate.getReferencePool().put(thumbnail));
-
-            if (mSuggestion != mArticle) return;
-
-            fadeThumbnailIn(thumbnail);
-        }
+        mSuggestionsBinder.updateOfflineBadgeVisibility(visible);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
new file mode 100644
index 0000000..9832169
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -0,0 +1,369 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions;
+
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.media.ThumbnailUtils;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.support.v4.text.BidiFormatter;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Callback;
+import org.chromium.base.Promise;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
+import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.download.ui.DownloadFilter;
+import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
+import org.chromium.chrome.browser.widget.TintedImageView;
+
+/**
+ * This class is directly connected to suggestions view holders. It takes over the responsibility
+ * of the view holder to update information on the views on the suggestion card.
+ */
+public class SuggestionsBinder {
+    private static final String ARTICLE_AGE_FORMAT_STRING = " - %s";
+    private static final int FADE_IN_ANIMATION_TIME_MS = 300;
+
+    private final ImageFetcher mImageFetcher;
+    private final SuggestionsUiDelegate mUiDelegate;
+
+    private final View mCardContainerView;
+    private final LinearLayout mTextLayout;
+    private final TextView mHeadlineTextView;
+    private final TextView mPublisherTextView;
+    private final TextView mSnippetTextView;
+    private final TextView mAgeTextView;
+    private final TintedImageView mThumbnailView;
+    private final ImageView mThumbnailVideoOverlay;
+    private final ImageView mOfflineBadge;
+    private final View mPublisherBar;
+
+    /** Total horizontal space occupied by the thumbnail, sum of its size and margin. */
+    private final int mThumbnailFootprintPx;
+    private final int mThumbnailSize;
+
+    @Nullable
+    private ImageFetcher.DownloadThumbnailRequest mThumbnailRequest;
+
+    private SnippetArticle mSuggestion;
+
+    public SuggestionsBinder(View cardContainerView, SuggestionsUiDelegate uiDelegate) {
+        mCardContainerView = cardContainerView;
+        mUiDelegate = uiDelegate;
+        mImageFetcher = uiDelegate.getImageFetcher();
+
+        mTextLayout = mCardContainerView.findViewById(R.id.text_layout);
+        mThumbnailView = mCardContainerView.findViewById(R.id.article_thumbnail);
+
+        mHeadlineTextView = mCardContainerView.findViewById(R.id.article_headline);
+        mPublisherTextView = mCardContainerView.findViewById(R.id.article_publisher);
+        mSnippetTextView = mCardContainerView.findViewById(R.id.article_snippet);
+        mAgeTextView = mCardContainerView.findViewById(R.id.article_age);
+        mThumbnailVideoOverlay =
+                mCardContainerView.findViewById(R.id.article_thumbnail_video_overlay);
+        mPublisherBar = mCardContainerView.findViewById(R.id.publisher_bar);
+        mOfflineBadge = mCardContainerView.findViewById(R.id.offline_icon);
+
+        boolean useLargeThumbnailLayout =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL);
+        mThumbnailSize = mCardContainerView.getResources().getDimensionPixelSize(
+                useLargeThumbnailLayout ? R.dimen.snippets_thumbnail_size_large
+                                        : R.dimen.snippets_thumbnail_size);
+        mThumbnailFootprintPx = mThumbnailSize
+                + mCardContainerView.getResources().getDimensionPixelSize(
+                          R.dimen.snippets_thumbnail_margin);
+    }
+
+    public void updateViewInformation(SnippetArticle suggestion) {
+        mSuggestion = suggestion;
+
+        mHeadlineTextView.setText(suggestion.mTitle);
+        mSnippetTextView.setText(suggestion.mPreviewText);
+        mPublisherTextView.setText(getPublisherString(suggestion));
+        mAgeTextView.setText(getArticleAge(suggestion));
+
+        setFavicon();
+        setThumbnail();
+    }
+
+    public void updateFieldsVisibility(boolean showHeadline, boolean showDescription,
+            boolean showThumbnail, boolean showThumbnailVideoOverlay, int headerMaxLines) {
+        mHeadlineTextView.setVisibility(showHeadline ? View.VISIBLE : View.GONE);
+        mHeadlineTextView.setMaxLines(headerMaxLines);
+        mSnippetTextView.setVisibility(showDescription ? View.VISIBLE : View.GONE);
+        mThumbnailView.setVisibility(showThumbnail ? View.VISIBLE : View.GONE);
+        mThumbnailVideoOverlay.setVisibility(showThumbnailVideoOverlay ? View.VISIBLE : View.GONE);
+
+        ViewGroup.MarginLayoutParams publisherBarParams =
+                (ViewGroup.MarginLayoutParams) mPublisherBar.getLayoutParams();
+
+        if (showDescription) {
+            publisherBarParams.topMargin = mPublisherBar.getResources().getDimensionPixelSize(
+                    R.dimen.snippets_publisher_margin_top_with_article_snippet);
+        } else if (showHeadline) {
+            // When we show a headline and not a description, we reduce the top margin of the
+            // publisher bar.
+            publisherBarParams.topMargin = mPublisherBar.getResources().getDimensionPixelSize(
+                    R.dimen.snippets_publisher_margin_top_without_article_snippet);
+        } else {
+            // When there is no headline and no description, we remove the top margin of the
+            // publisher bar.
+            publisherBarParams.topMargin = 0;
+        }
+
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)) {
+            mTextLayout.setMinimumHeight(showThumbnail ? mThumbnailSize : 0);
+        } else {
+            ApiCompatibilityUtils.setMarginEnd(
+                    publisherBarParams, showThumbnail ? mThumbnailFootprintPx : 0);
+        }
+
+        mPublisherBar.setLayoutParams(publisherBarParams);
+    }
+
+    public void updateOfflineBadgeVisibility(boolean visible) {
+        if (visible == (mOfflineBadge.getVisibility() == View.VISIBLE)) return;
+        mOfflineBadge.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    private void setFavicon() {
+        // The favicon of the publisher should match the TextView height.
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+        mPublisherTextView.measure(widthSpec, heightSpec);
+        final int publisherFaviconSizePx = mPublisherTextView.getMeasuredHeight();
+
+        // Set the favicon of the publisher.
+        // We start initialising with the default favicon to reserve the space and prevent the text
+        // from moving later.
+        setDefaultFaviconOnView(publisherFaviconSizePx);
+        Callback<Bitmap> faviconCallback = new Callback<Bitmap>() {
+            @Override
+            public void onResult(Bitmap bitmap) {
+                setFaviconOnView(bitmap, publisherFaviconSizePx);
+            }
+        };
+
+        mImageFetcher.makeFaviconRequest(mSuggestion, publisherFaviconSizePx, faviconCallback);
+    }
+
+    private void setThumbnail() {
+        // If there's still a pending thumbnail fetch, cancel it.
+        cancelThumbnailFetch();
+
+        // mThumbnailView's visibility is modified in updateFieldsVisibility().
+        if (mThumbnailView.getVisibility() != View.VISIBLE) return;
+
+        Bitmap thumbnail = mSuggestion.getThumbnailBitmap();
+        if (thumbnail != null) {
+            setThumbnailFromBitmap(thumbnail);
+            return;
+        }
+
+        if (mSuggestion.isDownload()) {
+            setDownloadThumbnail();
+            return;
+        }
+
+        // Temporarily set placeholder and then fetch the thumbnail from a provider.
+        mThumbnailView.setBackground(null);
+        mThumbnailView.setImageResource(R.drawable.ic_snippet_thumbnail_placeholder);
+        mThumbnailView.setTint(null);
+
+        // Fetch thumbnail for the current article.
+        mImageFetcher.makeArticleThumbnailRequest(
+                mSuggestion, new FetchThumbnailCallback(mSuggestion, mThumbnailSize));
+    }
+
+    private void setDownloadThumbnail() {
+        assert mSuggestion.isDownload();
+        if (!mSuggestion.isAssetDownload()) {
+            setThumbnailFromFileType(DownloadFilter.FILTER_PAGE);
+            return;
+        }
+
+        int fileType = DownloadFilter.fromMimeType(mSuggestion.getAssetDownloadMimeType());
+        if (fileType == DownloadFilter.FILTER_IMAGE) {
+            // For image downloads, attempt to fetch a thumbnail.
+            ImageFetcher.DownloadThumbnailRequest thumbnailRequest =
+                    mImageFetcher.makeDownloadThumbnailRequest(mSuggestion, mThumbnailSize);
+
+            Promise<Bitmap> thumbnailReceivedPromise = thumbnailRequest.getPromise();
+
+            if (thumbnailReceivedPromise.isFulfilled()) {
+                // If the thumbnail was cached, then it will be retrieved synchronously, the promise
+                // will be fulfilled and we can set the thumbnail immediately.
+                setThumbnailFromBitmap(thumbnailReceivedPromise.getResult());
+                return;
+            }
+
+            mThumbnailRequest = thumbnailRequest;
+
+            // Queue a callback to be called after the thumbnail is retrieved asynchronously.
+            thumbnailReceivedPromise.then(new FetchThumbnailCallback(mSuggestion, mThumbnailSize));
+        }
+
+        // Set a placeholder for the file type.
+        setThumbnailFromFileType(fileType);
+    }
+
+    private void setThumbnailFromBitmap(Bitmap thumbnail) {
+        assert thumbnail != null;
+        assert !thumbnail.isRecycled();
+        assert thumbnail.getWidth() <= mThumbnailSize || thumbnail.getHeight() <= mThumbnailSize;
+
+        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+        mThumbnailView.setBackground(null);
+        mThumbnailView.setImageBitmap(thumbnail);
+        mThumbnailView.setTint(null);
+    }
+
+    private void setThumbnailFromFileType(int fileType) {
+        int iconBackgroundColor = DownloadUtils.getIconBackgroundColor(mThumbnailView.getContext());
+        ColorStateList iconForegroundColorList =
+                DownloadUtils.getIconForegroundColorList(mThumbnailView.getContext());
+
+        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        mThumbnailView.setBackgroundColor(iconBackgroundColor);
+        mThumbnailView.setImageResource(
+                DownloadUtils.getIconResId(fileType, DownloadUtils.ICON_SIZE_36_DP));
+        mThumbnailView.setTint(iconForegroundColorList);
+    }
+
+    private void setDefaultFaviconOnView(int faviconSizePx) {
+        setFaviconOnView(
+                ApiCompatibilityUtils.getDrawable(
+                        mPublisherTextView.getContext().getResources(), R.drawable.default_favicon),
+                faviconSizePx);
+    }
+
+    private void setFaviconOnView(Bitmap image, int faviconSizePx) {
+        setFaviconOnView(new BitmapDrawable(mPublisherTextView.getContext().getResources(), image),
+                faviconSizePx);
+    }
+
+    private void setFaviconOnView(Drawable drawable, int faviconSizePx) {
+        drawable.setBounds(0, 0, faviconSizePx, faviconSizePx);
+        ApiCompatibilityUtils.setCompoundDrawablesRelative(
+                mPublisherTextView, drawable, null, null, null);
+        mPublisherTextView.setVisibility(View.VISIBLE);
+    }
+
+    private void cancelThumbnailFetch() {
+        if (mThumbnailRequest != null) {
+            mThumbnailRequest.cancel();
+            mThumbnailRequest = null;
+        }
+    }
+
+    private void fadeThumbnailIn(Bitmap thumbnail) {
+        assert mThumbnailView.getDrawable() != null;
+
+        mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+        mThumbnailView.setBackground(null);
+        mThumbnailView.setTint(null);
+        int duration = (int) (FADE_IN_ANIMATION_TIME_MS
+                * ChromeAnimation.Animation.getAnimationMultiplier());
+        if (duration == 0) {
+            mThumbnailView.setImageBitmap(thumbnail);
+            return;
+        }
+
+        // Cross-fade between the placeholder and the thumbnail. We cross-fade because the incoming
+        // image may have transparency and we don't want the previous image showing up behind.
+        Drawable[] layers = {mThumbnailView.getDrawable(),
+                new BitmapDrawable(mThumbnailView.getResources(), thumbnail)};
+        TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
+        mThumbnailView.setImageDrawable(transitionDrawable);
+        transitionDrawable.setCrossFadeEnabled(true);
+        transitionDrawable.startTransition(duration);
+    }
+
+    private static String getPublisherString(SnippetArticle suggestion) {
+        // We format the publisher here so that having a publisher name in an RTL language
+        // doesn't mess up the formatting on an LTR device and vice versa.
+        return BidiFormatter.getInstance().unicodeWrap(suggestion.mPublisher);
+    }
+
+    private static String getArticleAge(SnippetArticle suggestion) {
+        if (suggestion.mPublishTimestampMilliseconds == 0) return "";
+
+        // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this
+        // has never been called before it loads the current time zone from disk. In most likelihood
+        // this will have been called previously and the current time zone will have been cached,
+        // but in some cases (eg instrumentation tests) it will cause a strict mode violation.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        CharSequence relativeTimeSpan;
+        try {
+            long time = SystemClock.elapsedRealtime();
+            relativeTimeSpan =
+                    DateUtils.getRelativeTimeSpanString(suggestion.mPublishTimestampMilliseconds,
+                            System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS);
+            SuggestionsMetrics.recordDateFormattingDuration(SystemClock.elapsedRealtime() - time);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
+        // We add a dash before the elapsed time, e.g. " - 14 minutes ago".
+        return String.format(ARTICLE_AGE_FORMAT_STRING,
+                BidiFormatter.getInstance().unicodeWrap(relativeTimeSpan));
+    }
+
+    private class FetchThumbnailCallback extends Callback<Bitmap> {
+        private final SnippetArticle mCapturedSuggestion;
+        private final int mThumbnailSize;
+
+        FetchThumbnailCallback(SnippetArticle suggestion, int size) {
+            mCapturedSuggestion = suggestion;
+            mThumbnailSize = size;
+        }
+
+        @Override
+        public void onResult(Bitmap thumbnail) {
+            if (thumbnail == null) return; // Nothing to do, we keep the placeholder.
+
+            // We need to crop and scale the downloaded bitmap, as the ImageView we set it on won't
+            // be able to do so when using a TransitionDrawable (as opposed to the straight bitmap).
+            // That's a limitation of TransitionDrawable, which doesn't handle layers of varying
+            // sizes.
+            if (thumbnail.getHeight() != mThumbnailSize || thumbnail.getWidth() != mThumbnailSize) {
+                // Resize the thumbnail. If the provided bitmap is not cached or used anywhere else
+                // (that's true for bitmaps returned by SuggestionsSource for ARTICLE
+                // suggestions but not for those returned by ThumbnailProvider for DOWNLOADS for
+                // example), recycle the input image in the process.
+                thumbnail = ThumbnailUtils.extractThumbnail(thumbnail, mThumbnailSize,
+                        mThumbnailSize,
+                        mCapturedSuggestion.isArticle() ? ThumbnailUtils.OPTIONS_RECYCLE_INPUT : 0);
+            }
+
+            // Store the bitmap to skip the download task next time we display this snippet.
+            mCapturedSuggestion.setThumbnailBitmap(mUiDelegate.getReferencePool().put(thumbnail));
+
+            // Check whether the suggestions currently displayed in the view holder is the same as
+            // the suggestion whose thumbnail we have just fetched.
+            // This approach allows us to save the thumbnail in its corresponding SnippetArticle
+            // regardless of whether a new suggestion has been bound to the view holder. This way we
+            // don't have to cancel fetches and can use the retrieved thumbnail later on.
+            if (!TextUtils.equals(mCapturedSuggestion.getUrl(), mSuggestion.getUrl())) return;
+
+            fadeThumbnailIn(thumbnail);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
index 195549ba..e6a76278 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
@@ -113,6 +113,17 @@
     }
 
     /**
+     * Measures the amount of time it takes for date formatting in order to track StrictMode
+     * violations.
+     * See https://crbug.com/639877
+     * @param duration Duration of date formatting.
+     */
+    static void recordDateFormattingDuration(long duration) {
+        RecordHistogram.recordTimesHistogram(
+                "Android.StrictMode.SnippetUIBuildTime", duration, TimeUnit.MILLISECONDS);
+    }
+
+    /**
      * One-shot reporter that records the first time the user scrolls a {@link RecyclerView}. If it
      * should be reused, call {@link #reset()} to rearm it.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileView.java
index 316e0a4..c4040d93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileView.java
@@ -110,8 +110,8 @@
         assert mUrl.equals(tile.getUrl());
 
         if (tile.getIcon() != mIconView.getDrawable()) return false;
+        if (!tile.getTitle().equals(mTitleView.getText())) return false;
         if (tile.isOfflineAvailable() != (mBadgeView.getVisibility() == VISIBLE)) return false;
-        // We don't check the title since it's not likely to change, but that could also be done.
         return true;
     }
 
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 8b1fadd..8025440 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1024,6 +1024,7 @@
   "java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java",
   "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
   "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
+  "java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java",
   "java/src/org/chromium/chrome/browser/suggestions/Tile.java",
   "java/src/org/chromium/chrome/browser/suggestions/TileGrid.java",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4f9d7ce..ef2019ff 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -990,6 +990,8 @@
     "predictors/loading_predictor_factory.h",
     "predictors/loading_stats_collector.cc",
     "predictors/loading_stats_collector.h",
+    "predictors/preconnect_manager.cc",
+    "predictors/preconnect_manager.h",
     "predictors/predictor_database.cc",
     "predictors/predictor_database.h",
     "predictors/predictor_database_factory.cc",
@@ -1550,7 +1552,6 @@
     "//components/policy:generated",
     "//components/policy/core/browser",
     "//components/policy/proto",
-    "//components/precache/core",
     "//components/prefs:prefs",
     "//components/previews/core",
     "//components/profile_metrics",
@@ -3081,6 +3082,7 @@
       "//components/data_usage/android",
       "//components/payments/content/android",
       "//components/precache/content",
+      "//components/precache/core",
       "//components/resources:components_resources",
       "//components/toolbar",
       "//components/web_contents_delegate_android",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f75a00d..d0e8a37 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2382,11 +2382,6 @@
      flag_descriptions::kEnableNtpMostLikelyFaviconsFromServerDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(ntp_tiles::kNtpMostLikelyFaviconsFromServerFeature)},
-    {"enable-content-suggestions-categories",
-     flag_descriptions::kEnableContentSuggestionsCategoriesName,
-     flag_descriptions::kEnableContentSuggestionsCategoriesDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kContentSuggestionsCategories)},
     {"enable-content-suggestions-large-thumbnail",
      flag_descriptions::kEnableContentSuggestionsLargeThumbnailName,
      flag_descriptions::kEnableContentSuggestionsLargeThumbnailDescription,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index bcfcd7d9..6f97fcb 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -56,7 +56,6 @@
     &kChromeHomeFeature,
     &kChromeHomeExpandButton,
     &kChromeHomeSwipeLogic,
-    &kContentSuggestionsCategories,
     &kContentSuggestionsLargeThumbnail,
     &kContentSuggestionsVideoOverlay,
     &kContentSuggestionsSettings,
@@ -145,9 +144,6 @@
 const base::Feature kChromeHomeSwipeLogic{"ChromeHomeSwipeLogic",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kContentSuggestionsCategories{
-    "ContentSuggestionsCategories", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kContentSuggestionsLargeThumbnail{
     "ContentSuggestionsLargeThumbnail", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index fe80482..350b8c8 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -22,7 +22,6 @@
 extern const base::Feature kChromeHomeFeature;
 extern const base::Feature kChromeHomeExpandButton;
 extern const base::Feature kChromeHomeSwipeLogic;
-extern const base::Feature kContentSuggestionsCategories;
 extern const base::Feature kContentSuggestionsLargeThumbnail;
 extern const base::Feature kContentSuggestionsVideoOverlay;
 extern const base::Feature kContentSuggestionsSettings;
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.cc b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
index 42135b7..93693cf 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.cc
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
@@ -10,11 +10,15 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/thumbnails/thumbnail_list_source.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/ntp_tiles/metrics.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/rappor/rappor_service_impl.h"
@@ -39,21 +43,80 @@
 
 class JavaHomePageClient : public MostVisitedSites::HomePageClient {
  public:
-  JavaHomePageClient(JNIEnv* env, const JavaParamRef<jobject>& obj);
+  JavaHomePageClient(JNIEnv* env,
+                     const JavaParamRef<jobject>& obj,
+                     Profile* profile);
 
   bool IsHomePageEnabled() const override;
   bool IsNewTabPageUsedAsHomePage() const override;
-  GURL GetHomepageUrl() const override;
+  GURL GetHomePageUrl() const override;
+  void QueryHomePageTitle(TitleCallback title_callback) override;
 
  private:
+  void OnTitleEntryFound(bool success,
+                         const history::URLRow& row,
+                         const history::VisitVector& visits);
+
   ScopedJavaGlobalRef<jobject> client_;
+  Profile* profile_;
+
+  // Used in loading titles.
+  base::CancelableTaskTracker task_tracker_;
+  TitleCallback title_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(JavaHomePageClient);
 };
 
 JavaHomePageClient::JavaHomePageClient(JNIEnv* env,
-                                       const JavaParamRef<jobject>& obj)
-    : client_(env, obj) {}
+                                       const JavaParamRef<jobject>& obj,
+                                       Profile* profile)
+    : client_(env, obj), profile_(profile) {
+  DCHECK(profile);
+}
+
+void JavaHomePageClient::QueryHomePageTitle(
+    base::OnceCallback<void(const base::Optional<base::string16>&)>
+        title_callback) {
+  if (!title_callback_.is_null()) {
+    // A finished task always has to call the callback.
+    DCHECK(task_tracker_.HasTrackedTasks());
+    // If the last callback was not called, drop it as data would be stale.
+    task_tracker_.TryCancelAll();
+  }
+  DCHECK(!title_callback.is_null());
+  title_callback_ = std::move(title_callback);
+  GURL url = GetHomePageUrl();
+  if (url.is_empty()) {
+    std::move(title_callback_).Run(base::nullopt);
+    return;
+  }
+  history::HistoryService* const history_service =
+      HistoryServiceFactory::GetForProfileIfExists(
+          profile_, ServiceAccessType::EXPLICIT_ACCESS);
+  if (!history_service) {
+    std::move(title_callback_).Run(base::nullopt);
+    return;
+  }
+  // If the client is destroyed, the tracker will cancel this task automatically
+  // and the callback will not be called. Therefore, base::Unretained works.
+  // TODO(fhorschig): Bind the title_callback_ to |OnTitleEntryFound| as soon as
+  // |QueryURL| supports base::OnceCallback.
+  history_service->QueryURL(url,
+                            /*want_visits=*/false,
+                            base::Bind(&JavaHomePageClient::OnTitleEntryFound,
+                                       base::Unretained(this)),
+                            &task_tracker_);
+}
+
+void JavaHomePageClient::OnTitleEntryFound(bool success,
+                                           const history::URLRow& row,
+                                           const history::VisitVector& visits) {
+  if (!success) {
+    std::move(title_callback_).Run(base::nullopt);
+    return;
+  }
+  std::move(title_callback_).Run(row.title());
+}
 
 bool JavaHomePageClient::IsHomePageEnabled() const {
   return Java_HomePageClient_isHomePageEnabled(AttachCurrentThread(), client_);
@@ -64,7 +127,7 @@
                                                         client_);
 }
 
-GURL JavaHomePageClient::GetHomepageUrl() const {
+GURL JavaHomePageClient::GetHomePageUrl() const {
   base::android::ScopedJavaLocalRef<jstring> url =
       Java_HomePageClient_getHomePageUrl(AttachCurrentThread(), client_);
   if (url.is_null()) {
@@ -127,7 +190,8 @@
 }
 
 MostVisitedSitesBridge::MostVisitedSitesBridge(Profile* profile)
-    : most_visited_(ChromeMostVisitedSitesFactory::NewForProfile(profile)) {
+    : most_visited_(ChromeMostVisitedSitesFactory::NewForProfile(profile)),
+      profile_(profile) {
   // Register the thumbnails debugging page.
   // TODO(sfiera): find thumbnails a home. They don't belong here.
   content::URLDataSource::Add(profile, new ThumbnailListSource(profile));
@@ -161,7 +225,7 @@
     const base::android::JavaParamRef<jobject>& obj,
     const base::android::JavaParamRef<jobject>& j_client) {
   most_visited_->SetHomePageClient(
-      base::MakeUnique<JavaHomePageClient>(env, j_client));
+      base::MakeUnique<JavaHomePageClient>(env, j_client, profile_));
 }
 
 void MostVisitedSitesBridge::AddOrRemoveBlacklistedUrl(
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.h b/chrome/browser/android/ntp/most_visited_sites_bridge.h
index fe8fd5f..3eede8c 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.h
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.h
@@ -68,6 +68,7 @@
   std::unique_ptr<JavaObserver> java_observer_;
 
   std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_;
+  Profile* profile_;
 
   DISALLOW_COPY_AND_ASSIGN(MostVisitedSitesBridge);
 };
diff --git a/chrome/browser/cryptauth/chrome_cryptauth_service.cc b/chrome/browser/cryptauth/chrome_cryptauth_service.cc
index cd4673c..132e6d66 100644
--- a/chrome/browser/cryptauth/chrome_cryptauth_service.cc
+++ b/chrome/browser/cryptauth/chrome_cryptauth_service.cc
@@ -131,21 +131,12 @@
   return device_info;
 }
 
-std::string GetAccountIdImpl(Profile* profile) {
-#if defined(OS_CHROMEOS)
-  SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile);
-#else
-  SigninManager* manager = SigninManagerFactory::GetForProfile(profile);
-#endif
-  return manager ? manager->GetAuthenticatedAccountId() : std::string();
-}
-
 std::unique_ptr<cryptauth::CryptAuthClientFactory>
 CreateCryptAuthClientFactoryImpl(Profile* profile) {
   return base::MakeUnique<cryptauth::CryptAuthClientFactoryImpl>(
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-      GetAccountIdImpl(profile), profile->GetRequestContext(),
-      GetDeviceClassifierImpl());
+      SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(),
+      profile->GetRequestContext(), GetDeviceClassifierImpl());
 }
 
 std::unique_ptr<cryptauth::SecureMessageDelegate>
@@ -200,9 +191,14 @@
   ProfileOAuth2TokenService* token_service =
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
 
+  // Note: ChromeCryptAuthServiceFactory DependsOn(OAuth2TokenServiceFactory),
+  // so |token_service| is guaranteed to outlast this service.
+  SigninManagerBase* signin_manager =
+      SigninManagerFactory::GetForProfile(profile);
+
   return base::WrapUnique(new ChromeCryptAuthService(
       std::move(gcm_manager), std::move(device_manager),
-      std::move(enrollment_manager), profile, token_service));
+      std::move(enrollment_manager), profile, token_service, signin_manager));
 }
 
 ChromeCryptAuthService::ChromeCryptAuthService(
@@ -210,28 +206,42 @@
     std::unique_ptr<cryptauth::CryptAuthDeviceManager> device_manager,
     std::unique_ptr<cryptauth::CryptAuthEnrollmentManager> enrollment_manager,
     Profile* profile,
-    OAuth2TokenService* token_service)
+    OAuth2TokenService* token_service,
+    SigninManagerBase* signin_manager)
     : KeyedService(),
       cryptauth::CryptAuthService(),
       gcm_manager_(std::move(gcm_manager)),
       enrollment_manager_(std::move(enrollment_manager)),
       device_manager_(std::move(device_manager)),
       profile_(profile),
-      token_service_(token_service) {
+      token_service_(token_service),
+      signin_manager_(signin_manager) {
   gcm_manager_->StartListening();
 
-  if (!token_service_->RefreshTokenIsAvailable(GetAccountId())) {
+  if (!signin_manager_->IsAuthenticated()) {
+    PA_LOG(INFO) << "Profile is not authenticated yet; "
+                 << "waiting before starting CryptAuth managers.";
+    signin_manager_->AddObserver(this);
+    return;
+  }
+
+  std::string account_id = signin_manager_->GetAuthenticatedAccountId();
+  if (!token_service_->RefreshTokenIsAvailable(account_id)) {
     PA_LOG(INFO) << "Refresh token not yet available; "
                  << "waiting before starting CryptAuth managers.";
     token_service_->AddObserver(this);
-  } else {
-    PerformEnrollmentAndDeviceSync();
+    return;
   }
+
+  // Profile is authenticated and there is a refresh token available for the
+  // authenticated account id.
+  PerformEnrollmentAndDeviceSync();
 }
 
 ChromeCryptAuthService::~ChromeCryptAuthService() {}
 
 void ChromeCryptAuthService::Shutdown() {
+  signin_manager_->RemoveObserver(this);
   token_service_->RemoveObserver(this);
   enrollment_manager_.reset();
   device_manager_.reset();
@@ -253,7 +263,7 @@
 }
 
 std::string ChromeCryptAuthService::GetAccountId() {
-  return GetAccountIdImpl(profile_);
+  return signin_manager_->GetAuthenticatedAccountId();
 }
 
 std::unique_ptr<cryptauth::SecureMessageDelegate>
@@ -278,6 +288,20 @@
   enrollment_manager_->RemoveObserver(this);
 }
 
+void ChromeCryptAuthService::GoogleSigninSucceeded(
+    const std::string& account_id,
+    const std::string& username) {
+  signin_manager_->RemoveObserver(this);
+  if (!token_service_->RefreshTokenIsAvailable(account_id)) {
+    PA_LOG(INFO) << "Refresh token not yet available; "
+                 << "waiting before starting CryptAuth managers.";
+    token_service_->AddObserver(this);
+    return;
+  }
+
+  PerformEnrollmentAndDeviceSync();
+}
+
 void ChromeCryptAuthService::OnRefreshTokenAvailable(
     const std::string& account_id) {
   if (account_id == GetAccountId()) {
@@ -287,6 +311,8 @@
 }
 
 void ChromeCryptAuthService::PerformEnrollmentAndDeviceSync() {
+  DCHECK(signin_manager_->IsAuthenticated());
+  DCHECK(token_service_->RefreshTokenIsAvailable(GetAccountId()));
   if (enrollment_manager_->IsEnrollmentValid()) {
     device_manager_->Start();
   } else {
diff --git a/chrome/browser/cryptauth/chrome_cryptauth_service.h b/chrome/browser/cryptauth/chrome_cryptauth_service.h
index 9aa6c11e..478cea7e 100644
--- a/chrome/browser/cryptauth/chrome_cryptauth_service.h
+++ b/chrome/browser/cryptauth/chrome_cryptauth_service.h
@@ -12,6 +12,7 @@
 #include "components/cryptauth/cryptauth_service.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/core/browser/signin_manager_base.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 
 class Profile;
@@ -25,7 +26,8 @@
     : public KeyedService,
       public cryptauth::CryptAuthService,
       public cryptauth::CryptAuthEnrollmentManager::Observer,
-      public OAuth2TokenService::Observer {
+      public OAuth2TokenService::Observer,
+      public SigninManagerBase::Observer {
  public:
   static std::unique_ptr<ChromeCryptAuthService> Create(Profile* profile);
   ~ChromeCryptAuthService() override;
@@ -56,12 +58,17 @@
       std::unique_ptr<cryptauth::CryptAuthDeviceManager> device_manager,
       std::unique_ptr<cryptauth::CryptAuthEnrollmentManager> enrollment_manager,
       Profile* profile,
-      OAuth2TokenService* token_service);
+      OAuth2TokenService* token_service,
+      SigninManagerBase* signin_manager);
 
  private:
   // OAuth2TokenService::Observer:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
 
+  // SigninManagerBase::Observer:
+  void GoogleSigninSucceeded(const std::string& account_id,
+                             const std::string& username) override;
+
   void PerformEnrollmentAndDeviceSync();
 
   std::unique_ptr<cryptauth::CryptAuthGCMManager> gcm_manager_;
@@ -69,6 +76,7 @@
   std::unique_ptr<cryptauth::CryptAuthDeviceManager> device_manager_;
   Profile* profile_;
   OAuth2TokenService* token_service_;
+  SigninManagerBase* signin_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeCryptAuthService);
 };
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
index b875323..cf6a8ec 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
 
+#include "base/task_scheduler/post_task.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/plugins/plugin_data_remover_helper.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
@@ -283,8 +284,10 @@
       ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA) {
     // If we're being asked to remove plugin data, check whether it's actually
     // supported.
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
+    PostTaskWithTraits(
+        FROM_HERE,
+        {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+         base::TaskPriority::USER_VISIBLE},
         base::BindOnce(
             &BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported,
             this, PluginPrefs::GetForProfile(GetProfile())));
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 221a679..0fb9012c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -26,8 +26,8 @@
     "Enable Material Design policy page";
 
 const char kEnableMaterialDesignPolicyPageDescription[] =
-    "If enabled, the chrome://md-policy URL loads the Material Design "
-    "policy page.";
+    "If enabled, the chrome://md-policy URL loads the Material Design policy "
+    "page.";
 
 //  Material Design version of chrome://extensions
 
@@ -44,8 +44,7 @@
     "Enable Material Design feedback";
 
 const char kEnableMaterialDesignFeedbackDescription[] =
-    "If enabled, reporting an issue will load the Material Design feedback "
-    "UI.";
+    "If enabled, reporting an issue will load the Material Design feedback UI.";
 
 const char kContextualSuggestionsCarouselName[] =
     "Enable Contextual Suggestions";
@@ -67,8 +66,8 @@
     "Use Blink's zoom for device scale factor.";
 
 const char kEnableUseZoomForDsfDescription[] =
-    "If enabled, Blink uses its zooming mechanism to scale content for "
-    "device scale factor.";
+    "If enabled, Blink uses its zooming mechanism to scale content for device "
+    "scale factor.";
 
 const char kEnableUseZoomForDsfChoiceDefault[] = "Default";
 
@@ -122,16 +121,16 @@
 const char kPrintPdfAsImageName[] = "Print Pdf as Image";
 
 const char kPrintPdfAsImageDescription[] =
-    "If enabled, an option to print PDF files as images will be available "
-    "in print preview.";
+    "If enabled, an option to print PDF files as images will be available in "
+    "print preview.";
 #endif
 
 #if !defined(DISABLE_NACL)
 
 const char kNaclName[] = "Native Client";
 const char kNaclDescription[] =
-    "Support Native Client for all web applications, even those that were "
-    "not installed from the Chrome Web Store.";
+    "Support Native Client for all web applications, even those that were not "
+    "installed from the Chrome Web Store.";
 
 const char kNaclDebugName[] = "Native Client GDB-based debugging";
 const char kNaclDebugDescription[] =
@@ -146,8 +145,8 @@
     "Restrict Native Client GDB-based debugging by pattern";
 const char kNaclDebugMaskDescription[] =
     "Restricts Native Client application GDB-based debugging by URL of "
-    "manifest file. Native Client GDB-based debugging must be enabled for "
-    "this option to work.";
+    "manifest file. Native Client GDB-based debugging must be enabled for this "
+    "option to work.";
 const char kNaclDebugMaskChoiceDebugAll[] = "Debug everything.";
 const char kNaclDebugMaskChoiceExcludeUtilsPnacl[] =
     "Debug everything except secure shell and the PNaCl translator.";
@@ -157,12 +156,12 @@
 #endif
 
 const char kEnableHttpFormWarningName[] =
-    "Show in-form warnings for sensitive fields when the top-level page is "
-    "not HTTPS";
+    "Show in-form warnings for sensitive fields when the top-level page is not "
+    "HTTPS";
 
 const char kEnableHttpFormWarningDescription[] =
-    "Attaches a warning UI to any password or credit card fields detected "
-    "when the top-level page is not HTTPS";
+    "Attaches a warning UI to any password or credit card fields detected when "
+    "the top-level page is not HTTPS";
 
 const char kMarkHttpAsName[] = "Mark non-secure origins as non-secure";
 
@@ -181,14 +180,14 @@
     "Material Design Incognito NTP.";
 
 extern const char kMaterialDesignIncognitoNTPDescription[] =
-    "If enabled, the Incognito New Tab page uses the new material design "
-    "with a better readable text.";
+    "If enabled, the Incognito New Tab page uses the new material design with "
+    "a better readable text.";
 
 const char kSavePageAsMhtmlName[] = "Save Page as MHTML";
 
 const char kSavePageAsMhtmlDescription[] =
-    "Enables saving pages as MHTML: a single text file containing HTML and "
-    "all sub-resources.";
+    "Enables saving pages as MHTML: a single text file containing HTML and all "
+    "sub-resources.";
 
 //  Flag and values for MHTML Generator options lab.
 
@@ -212,9 +211,9 @@
 const char kCloudPrintXpsName[] = "XPS in Google Cloud Print";
 
 const char kCloudPrintXpsDescription[] =
-    "XPS enables advanced options for classic printers connected to the "
-    "Cloud Print with Chrome. Printers must be re-connected after changing "
-    "this flag.";
+    "XPS enables advanced options for classic printers connected to the Cloud "
+    "Print with Chrome. Printers must be re-connected after changing this "
+    "flag.";
 
 #endif  // defined(OS_WIN)
 
@@ -257,8 +256,8 @@
 
 const char kSecondaryUiMdDescription[] =
     "Extends the --top-chrome-md setting to secondary UI (bubbles, dialogs, "
-    "etc.). On Mac, this enables MacViews, which uses toolkit-views for "
-    "native browser dialogs.";
+    "etc.). On Mac, this enables MacViews, which uses toolkit-views for native "
+    "browser dialogs.";
 
 const char kScrollPredictionDescription[] =
     "Predicts the finger's future position during scrolls allowing time to "
@@ -309,14 +308,14 @@
 const char kTouchAdjustmentName[] = "Touch adjustment";
 
 const char kTouchAdjustmentDescription[] =
-    "Refine the position of a touch gesture in order to compensate for "
-    "touches having poor resolution compared to a mouse.";
+    "Refine the position of a touch gesture in order to compensate for touches "
+    "having poor resolution compared to a mouse.";
 
 const char kCompositedLayerBordersName[] = "Composited render layer borders";
 
 const char kCompositedLayerBordersDescription[] =
-    "Renders a border around composited Render Layers to help debug and "
-    "study layer compositing.";
+    "Renders a border around composited Render Layers to help debug and study "
+    "layer compositing.";
 
 const char kGlCompositedTextureQuadBordersName[] =
     "GL composited texture quad borders";
@@ -328,8 +327,8 @@
 const char kShowOverdrawFeedbackName[] = "Show overdraw feedback";
 
 const char kShowOverdrawFeedbackDescription[] =
-    "Visualize overdraw by color-coding elements based on if they have "
-    "other elements drawn underneath.";
+    "Visualize overdraw by color-coding elements based on if they have other "
+    "elements drawn underneath.";
 
 const char kUiPartialSwapName[] = "Partial swap";
 
@@ -346,8 +345,8 @@
 const char kInertVisualViewportName[] = "Inert visual viewport.";
 
 const char kInertVisualViewportDescription[] =
-    "Experiment to have all APIs reflect the layout viewport. This will "
-    "make window.scroll properties relative to the layout viewport.";
+    "Experiment to have all APIs reflect the layout viewport. This will make "
+    "window.scroll properties relative to the layout viewport.";
 
 const char kInProductHelpDemoModeChoiceName[] = "In-Product Help Demo Mode";
 
@@ -391,8 +390,8 @@
 const char kExtensionsOnChromeUrlsName[] = "Extensions on chrome:// URLs";
 
 const char kExtensionsOnChromeUrlsDescription[] =
-    "Enables running extensions on chrome:// URLs, where extensions "
-    "explicitly request this permission.";
+    "Enables running extensions on chrome:// URLs, where extensions explicitly "
+    "request this permission.";
 
 const char kExperimentalFullscreenExitUIName[] =
     "Experimental fullscreen exit UI";
@@ -410,8 +409,8 @@
 const char kFetchKeepaliveTimeoutSettingName[] =
     "Fetch API keepalive timeout setting";
 const char kFetchKeepaliveTimeoutSettingDescription[] =
-    "This is for setting "
-    "the timeout value for Fetch API with keepalive option and SendBeacon";
+    "This is for setting the timeout value for Fetch API with keepalive option "
+    "and SendBeacon";
 
 const char kUserConsentForExtensionScriptsName[] =
     "User consent for extension scripts";
@@ -455,8 +454,8 @@
     "Contextual Search - URL Actions";
 
 const char kContextualSearchUrlActionsDescription[] =
-    "Whether or not URL actions using Contextual Cards data in the "
-    "Contextual Search Bar is enabled.";
+    "Whether or not URL actions using Contextual Cards data in the Contextual "
+    "Search Bar is enabled.";
 
 #endif  // defined(OS_ANDROID)
 
@@ -468,21 +467,21 @@
 const char kOverlayScrollbarsName[] = "Overlay Scrollbars";
 
 const char kOverlayScrollbarsDescription[] =
-    "Enable the experimental overlay scrollbars implementation. You must "
-    "also enable threaded compositing to have the scrollbars animate.";
+    "Enable the experimental overlay scrollbars implementation. You must also "
+    "enable threaded compositing to have the scrollbars animate.";
 
 const char kShowAutofillTypePredictionsName[] = "Show Autofill predictions";
 
 const char kShowAutofillTypePredictionsDescription[] =
-    "Annotates web forms with Autofill field type predictions as "
-    "placeholder text.";
+    "Annotates web forms with Autofill field type predictions as placeholder "
+    "text.";
 
 const char kTcpFastOpenName[] = "TCP Fast Open";
 
 const char kTcpFastOpenDescription[] =
-    "Enable the option to send extra authentication information in the "
-    "initial SYN packet for a previously connected client, allowing faster "
-    "data send start.";
+    "Enable the option to send extra authentication information in the initial "
+    "SYN packet for a previously connected client, allowing faster data send "
+    "start.";
 
 const char kTouchDragDropName[] = "Touch initiated drag and drop";
 
@@ -493,8 +492,8 @@
 const char kTouchSelectionStrategyName[] = "Touch text selection strategy";
 
 const char kTouchSelectionStrategyDescription[] =
-    "Controls how text selection granularity changes when touch text "
-    "selection handles are dragged. Non-default behavior is experimental.";
+    "Controls how text selection granularity changes when touch text selection "
+    "handles are dragged. Non-default behavior is experimental.";
 
 const char kTouchSelectionStrategyCharacter[] = "Character";
 
@@ -504,8 +503,7 @@
     "Use Google Payments sandbox servers";
 
 const char kWalletServiceUseSandboxDescription[] =
-    "For developers: use the sandbox service for Google Payments API "
-    "calls.";
+    "For developers: use the sandbox service for Google Payments API calls.";
 
 const char kOverscrollHistoryNavigationName[] = "Overscroll history navigation";
 
@@ -559,21 +557,19 @@
 const char kWebrtcHwVP8EncodingName[] = "WebRTC hardware vp8 video encoding";
 
 const char kWebrtcHwVP8EncodingDescription[] =
-    "Support in WebRTC for encoding vp8 video streams using platform "
-    "hardware.";
+    "Support in WebRTC for encoding vp8 video streams using platform hardware.";
 
 const char kWebrtcSrtpAesGcmName[] =
     "Negotiation with GCM cipher suites for SRTP in WebRTC";
 
 const char kWebrtcSrtpAesGcmDescription[] =
-    "When enabled, WebRTC will try to negotiate GCM cipher suites for "
-    "SRTP.";
+    "When enabled, WebRTC will try to negotiate GCM cipher suites for SRTP.";
 
 const char kWebrtcStunOriginName[] = "WebRTC Stun origin header";
 
 const char kWebrtcStunOriginDescription[] =
-    "When enabled, Stun messages generated by WebRTC will contain the "
-    "Origin header.";
+    "When enabled, Stun messages generated by WebRTC will contain the Origin "
+    "header.";
 
 const char kWebrtcEchoCanceller3Name[] = "WebRTC Echo Canceller 3.";
 
@@ -595,9 +591,9 @@
     "WebRTC H.264 software video encoder/decoder";
 
 const char kWebrtcH264WithOpenh264FfmpegDescription[] =
-    "When enabled, an H.264 software video encoder/decoder pair is "
-    "included. If a hardware encoder/decoder is also available it may be "
-    "used instead of this encoder/decoder.";
+    "When enabled, an H.264 software video encoder/decoder pair is included. "
+    "If a hardware encoder/decoder is also available it may be used instead of "
+    "this encoder/decoder.";
 
 #endif  // BUILDFLAG(ENABLE_WEBRTC)
 
@@ -613,8 +609,8 @@
     "WebVR experimental rendering optimizations";
 
 const char kWebvrExperimentalRenderingDescription[] =
-    "Enabling this option activates experimental rendering path "
-    "optimizations for WebVR.";
+    "Enabling this option activates experimental rendering path optimizations "
+    "for WebVR.";
 
 #endif  // BUILDFLAG(ENABLE_VR)
 
@@ -667,8 +663,8 @@
     "Document Level Event Listeners Passive Default";
 
 const char kPassiveEventListenersDueToFlingDescription[] =
-    "Forces touchstart, and first touchmove per scroll event listeners "
-    "during fling to be treated as passive.";
+    "Forces touchstart, and first touchmove per scroll event listeners during "
+    "fling to be treated as passive.";
 
 const char kPassiveEventListenersDueToFlingName[] =
     "Touch Event Listeners Passive Default During Fling";
@@ -681,11 +677,10 @@
     "Passive Event Listener Override";
 
 const char kPassiveEventListenerDefaultDescription[] =
-    "Forces touchstart, touchmove, mousewheel and wheel event listeners "
-    "(which haven't requested otherwise) to be treated as passive. This "
-    "will break touch/wheel behavior on some websites but is useful for "
-    "demonstrating the potential performance benefits of adopting passive "
-    "event listeners.";
+    "Forces touchstart, touchmove, mousewheel and wheel event listeners (which "
+    "haven't requested otherwise) to be treated as passive. This will break "
+    "touch/wheel behavior on some websites but is useful for demonstrating the "
+    "potential performance benefits of adopting passive event listeners.";
 
 const char kImportantSitesInCbdName[] =
     "Important sites options in clear browsing data dialog";
@@ -767,10 +762,9 @@
     "Latest stable JavaScript features";
 
 const char kJavascriptHarmonyShippingDescription[] =
-    "Some web pages use legacy or non-standard JavaScript extensions that "
-    "may conflict with the latest JavaScript features. This flag allows "
-    "disabling support of those features for compatibility with such "
-    "pages.";
+    "Some web pages use legacy or non-standard JavaScript extensions that may "
+    "conflict with the latest JavaScript features. This flag allows disabling "
+    "support of those features for compatibility with such pages.";
 
 const char kJavascriptHarmonyName[] = "Experimental JavaScript";
 
@@ -805,8 +799,7 @@
     "Download button when opening a page with media url.";
 
 const char kMediaDocumentDownloadButtonDescription[] =
-    "Allow a download button to show up when opening a page with media "
-    "url.";
+    "Allow a download button to show up when opening a page with media url.";
 
 #endif  // defined(OS_ANDROID)
 
@@ -848,9 +841,9 @@
 
 const char kExperimentalSecurityFeaturesDescription[] =
     "Enables several security features that will likely break one or more "
-    "pages that you visit on a daily basis. Strict mixed content checking, "
-    "for example. And locking powerful features to secure contexts. This "
-    "flag will probably annoy you.";
+    "pages that you visit on a daily basis. Strict mixed content checking, for "
+    "example. And locking powerful features to secure contexts. This flag will "
+    "probably annoy you.";
 
 const char kExperimentalWebPlatformFeaturesName[] =
     "Experimental Web Platform features";
@@ -861,14 +854,13 @@
 const char kOriginTrialsName[] = "Origin Trials";
 
 const char kOriginTrialsDescription[] =
-    "Enables origin trials for controlling access to feature/API "
-    "experiments.";
+    "Enables origin trials for controlling access to feature/API experiments.";
 
 const char kBleAdvertisingInExtensionsName[] = "BLE Advertising in Chrome Apps";
 
 const char kBleAdvertisingInExtensionsDescription[] =
-    "Enables BLE Advertising in Chrome Apps. BLE Advertising might "
-    "interfere with regular use of Bluetooth Low Energy features.";
+    "Enables BLE Advertising in Chrome Apps. BLE Advertising might interfere "
+    "with regular use of Bluetooth Low Energy features.";
 
 const char kDevtoolsExperimentsName[] = "Developer Tools experiments";
 
@@ -916,9 +908,9 @@
     "Reduce default 'referer' header granularity.";
 
 const char kReducedReferrerGranularityDescription[] =
-    "If a page hasn't set an explicit referrer policy, setting this flag "
-    "will reduce the amount of information in the 'referer' header for "
-    "cross-origin requests.";
+    "If a page hasn't set an explicit referrer policy, setting this flag will "
+    "reduce the amount of information in the 'referer' header for cross-origin "
+    "requests.";
 
 #if defined(OS_CHROMEOS)
 
@@ -975,34 +967,33 @@
 
 const char kRequestTabletSiteDescription[] =
     "Allows the user to request tablet site. Web content is often optimized "
-    "for tablet devices. When this option is selected the user agent string "
-    "is changed to indicate a tablet device. Web content optimized for "
-    "tablets is received there after for the current tab.";
+    "for tablet devices. When this option is selected the user agent string is "
+    "changed to indicate a tablet device. Web content optimized for tablets is "
+    "received there after for the current tab.";
 
 const char kDebugPackedAppName[] = "Debugging for packed apps";
 
 const char kDebugPackedAppDescription[] =
-    "Enables debugging context menu options such as Inspect Element for "
-    "packed applications.";
+    "Enables debugging context menu options such as Inspect Element for packed "
+    "applications.";
 
 const char kDropSyncCredentialName[] =
     "Drop sync credentials from password manager";
 
 const char kDropSyncCredentialDescription[] =
-    "The password manager will not offer to save the credential used to "
-    "sync.";
+    "The password manager will not offer to save the credential used to sync.";
 
 const char kPasswordGenerationName[] = "Password generation";
 
 const char kPasswordGenerationDescription[] =
-    "Allow the user to have Chrome generate passwords when it detects "
-    "account creation pages.";
+    "Allow the user to have Chrome generate passwords when it detects account "
+    "creation pages.";
 
 const char kPasswordForceSavingName[] = "Force-saving of passwords";
 
 const char kPasswordForceSavingDescription[] =
-    "Allow the user to manually enforce password saving instead of relying "
-    "on password manager's heuristics.";
+    "Allow the user to manually enforce password saving instead of relying on "
+    "password manager's heuristics.";
 
 const char kManualPasswordGenerationName[] = "Manual password generation.";
 
@@ -1054,9 +1045,9 @@
 const char kPushApiBackgroundModeName[] = "Enable Push API background mode";
 
 const char kPushApiBackgroundModeDescription[] =
-    "Enable background mode for the Push API. This allows Chrome to "
-    "continue running after the last window is closed, and to launch at OS "
-    "startup, if the Push API needs it.";
+    "Enable background mode for the Push API. This allows Chrome to continue "
+    "running after the last window is closed, and to launch at OS startup, if "
+    "the Push API needs it.";
 
 const char kEnableNavigationTracingName[] = "Enable navigation tracing";
 
@@ -1083,9 +1074,8 @@
 const char kTraceUploadUrlDescription[] =
     "This is to be used in conjunction with the enable-navigation-tracing "
     "flag. Please select the label that best describes the recorded traces. "
-    "This will choose the destination the traces are uploaded to. If you "
-    "are not sure, select other. If left empty, no traces will be "
-    "uploaded.";
+    "This will choose the destination the traces are uploaded to. If you are "
+    "not sure, select other. If left empty, no traces will be uploaded.";
 
 const char kDisableAudioForDesktopShareName[] =
     "Disable Audio For Desktop Share";
@@ -1122,8 +1112,8 @@
 const char kSyncAppListName[] = "App Launcher sync";
 
 const char kSyncAppListDescription[] =
-    "Enable App Launcher sync. This also enables Folders where available "
-    "(non OSX).";
+    "Enable App Launcher sync. This also enables Folders where available (non "
+    "OSX).";
 
 const char kDriveSearchInChromeLauncherName[] =
     "Drive Search in Chrome App Launcher";
@@ -1144,8 +1134,7 @@
     "V8 caching strategy for CacheStorage.";
 
 const char kV8CacheStrategiesForCacheStorageDescription[] =
-    "Caching strategy of scripts in CacheStorage for the V8 JavaScript "
-    "engine.";
+    "Caching strategy of scripts in CacheStorage for the V8 JavaScript engine.";
 
 const char kV8CacheStrategiesForCacheStorageNormal[] = "Normal";
 
@@ -1169,8 +1158,8 @@
 
 const char kDataReductionProxyLoFiDescription[] =
     "Forces Data Saver Lo-Fi mode to be always enabled, enabled only on "
-    "cellular connections, or disabled. Data Saver must be enabled for "
-    "Lo-Fi mode to be used.";
+    "cellular connections, or disabled. Data Saver must be enabled for Lo-Fi "
+    "mode to be used.";
 
 const char kDataReductionProxyLoFiAlwaysOn[] = "Always on";
 
@@ -1209,9 +1198,9 @@
     "Data Saver 1 MB Savings Promo";
 
 const char kEnableDataReductionProxySavingsPromoDescription[] =
-    "Enable a Data Saver promo for 1 MB of savings. If Data Saver has "
-    "already saved 1 MB of data, then the promo will not be shown. Data "
-    "Saver must be enabled for the promo to be shown.";
+    "Enable a Data Saver promo for 1 MB of savings. If Data Saver has already "
+    "saved 1 MB of data, then the promo will not be shown. Data Saver must be "
+    "enabled for the promo to be shown.";
 
 #if defined(OS_ANDROID)
 
@@ -1242,8 +1231,8 @@
 const char kLcdTextName[] = "LCD text antialiasing";
 
 const char kLcdTextDescription[] =
-    "If disabled, text is rendered with grayscale antialiasing instead of "
-    "LCD (subpixel) when doing accelerated compositing.";
+    "If disabled, text is rendered with grayscale antialiasing instead of LCD "
+    "(subpixel) when doing accelerated compositing.";
 
 const char kDistanceFieldTextName[] = "Distance field text";
 
@@ -1297,9 +1286,9 @@
     "Reset the App Launcher install state on every restart.";
 
 const char kResetAppListInstallStateDescription[] =
-    "Reset the App Launcher install state on every restart. While this flag "
-    "is set, Chrome will forget the launcher has been installed each time "
-    "it starts. This is used for testing the App Launcher install flow.";
+    "Reset the App Launcher install state on every restart. While this flag is "
+    "set, Chrome will forget the launcher has been installed each time it "
+    "starts. This is used for testing the App Launcher install flow.";
 
 #if defined(OS_CHROMEOS)
 
@@ -1329,8 +1318,7 @@
     "Allow tab detaching in fullscreen";
 
 const char kTabDetachingInFullscreenDescription[] =
-    "Allow tabs to detach from the tabstrip when in fullscreen mode on "
-    "Mac.";
+    "Allow tabs to detach from the tabstrip when in fullscreen mode on Mac.";
 
 const char kFullscreenToolbarRevealName[] =
     "Enables the toolbar in fullscreen to reveal itself.";
@@ -1356,8 +1344,8 @@
     "Quit notification for hosted apps";
 
 const char kHostedAppQuitNotificationDescription[] =
-    "Display a notification when quitting Chrome if hosted apps are "
-    "currently running.";
+    "Display a notification when quitting Chrome if hosted apps are currently "
+    "running.";
 
 #if defined(OS_ANDROID)
 
@@ -1396,8 +1384,8 @@
 const char kPermissionActionReportingName[] = "Permission Action Reporting";
 
 const char kPermissionActionReportingDescription[] =
-    "Enables permission action reporting to Safe Browsing servers for opted "
-    "in users.";
+    "Enables permission action reporting to Safe Browsing servers for opted in "
+    "users.";
 
 const char kPermissionsBlacklistName[] = "Permissions Blacklist";
 
@@ -1409,28 +1397,28 @@
 
 const char kThreadedScrollingDescription[] =
     "Threaded handling of scroll-related input events. Disabling this will "
-    "force all such scroll events to be handled on the main thread. Note "
-    "that this can dramatically hurt scrolling performance of most websites "
-    "and is intended for testing purposes only.";
+    "force all such scroll events to be handled on the main thread. Note that "
+    "this can dramatically hurt scrolling performance of most websites and is "
+    "intended for testing purposes only.";
 
 const char kHarfbuzzRendertextName[] = "HarfBuzz for UI text";
 
 const char kHarfbuzzRendertextDescription[] =
-    "Enable cross-platform HarfBuzz layout engine for UI text. Doesn't "
-    "affect web content.";
+    "Enable cross-platform HarfBuzz layout engine for UI text. Doesn't affect "
+    "web content.";
 
 const char kEmbeddedExtensionOptionsName[] = "Embedded extension options";
 
 const char kEmbeddedExtensionOptionsDescription[] =
-    "Display extension options as an embedded element in "
-    "chrome://extensions rather than opening a new tab.";
+    "Display extension options as an embedded element in chrome://extensions "
+    "rather than opening a new tab.";
 
 const char kTabAudioMutingName[] = "Tab audio muting UI control";
 
 const char kTabAudioMutingDescription[] =
-    "When enabled, the audio indicators in the tab strip double as tab "
-    "audio mute controls. This also adds commands in the tab context menu "
-    "for quickly muting multiple selected tabs.";
+    "When enabled, the audio indicators in the tab strip double as tab audio "
+    "mute controls. This also adds commands in the tab context menu for "
+    "quickly muting multiple selected tabs.";
 
 const char kEasyUnlockBluetoothLowEnergyDiscoveryName[] =
     "Smart Lock Bluetooth Low Energy Discovery";
@@ -1444,17 +1432,17 @@
     "Smart Lock proximity detection";
 
 const char kEasyUnlockProximityDetectionDescription[] =
-    "Enables a Smart Lock setting that restricts unlocking to only work "
-    "when your phone is very close to (roughly, within an arm's length of) "
-    "the Chrome device.";
+    "Enables a Smart Lock setting that restricts unlocking to only work when "
+    "your phone is very close to (roughly, within an arm's length of) the "
+    "Chrome device.";
 
 const char kWifiCredentialSyncName[] = "WiFi credential sync";
 
 const char kWifiCredentialSyncDescription[] =
-    "Enables synchronizing WiFi network settings across devices. When "
-    "enabled, the WiFi credential datatype is registered with Chrome Sync, "
-    "and WiFi credentials are synchronized subject to user preferences. "
-    "(See also, chrome://settings/syncSetup.)";
+    "Enables synchronizing WiFi network settings across devices. When enabled, "
+    "the WiFi credential datatype is registered with Chrome Sync, and WiFi "
+    "credentials are synchronized subject to user preferences. (See also, "
+    "chrome://settings/syncSetup.)";
 
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 
@@ -1465,8 +1453,8 @@
 
 const char kDatasaverPromptDescription[] =
     "Enables a prompt, which appears when a cellular network connection is "
-    "detected, to take the user to the Data Saver extension page on Chrome "
-    "Web Store.";
+    "detected, to take the user to the Data Saver extension page on Chrome Web "
+    "Store.";
 
 const char kDatasaverPromptDemoMode[] = "Demo mode";
 
@@ -1476,9 +1464,9 @@
 
 const char kTrySupportedChannelLayoutsDescription[] =
     "Causes audio output streams to check if channel layouts other than the "
-    "default hardware layout are available. Turning this on will allow the "
-    "OS to do stereo to surround expansion if supported. May expose third "
-    "party driver bugs, use with caution.";
+    "default hardware layout are available. Turning this on will allow the OS "
+    "to do stereo to surround expansion if supported. May expose third party "
+    "driver bugs, use with caution.";
 
 #if defined(OS_MACOSX)
 
@@ -1511,8 +1499,8 @@
 const char kAppWindowCyclingDescription[] =
     "Changes the behavior of Cmd+` when a Chrome App becomes active. When "
     "enabled, Chrome Apps will not be cycled when Cmd+` is pressed from a "
-    "browser window, and browser windows will not be cycled when a Chrome "
-    "App is active.";
+    "browser window, and browser windows will not be cycled when a Chrome App "
+    "is active.";
 
 #endif  // defined(OS_MACOSX)
 
@@ -1531,8 +1519,8 @@
     "Simplified full screen / mouse lock UI.";
 
 const char kSimplifiedFullscreenUiDescription[] =
-    "A simplified new user experience when entering page-triggered full "
-    "screen or mouse pointer lock states.";
+    "A simplified new user experience when entering page-triggered full screen "
+    "or mouse pointer lock states.";
 
 const char kExperimentalKeyboardLockUiName[] = "Experimental keyboard lock UI.";
 
@@ -1580,8 +1568,8 @@
     "Main frame's domContentLoaded (iframes ignored).";
 
 const char kProgressBarCompletionResourcesBeforeDclAndSameOriginIframes[] =
-    "domContentLoaded and all resources loads started before "
-    "domContentLoaded (main frame and same origin iframes).";
+    "domContentLoaded and all resources loads started before domContentLoaded "
+    "(main frame and same origin iframes).";
 
 #endif  // defined(OS_ANDROID)
 
@@ -1589,8 +1577,8 @@
     "Block scripts loaded via document.write";
 
 const char kDisallowDocWrittenScriptsUiDescription[] =
-    "Disallows fetches for third-party parser-blocking scripts inserted "
-    "into the main frame via document.write.";
+    "Disallows fetches for third-party parser-blocking scripts inserted into "
+    "the main frame via document.write.";
 
 #if defined(OS_WIN)
 
@@ -1618,8 +1606,8 @@
     "Enable offering upload of Autofilled credit cards";
 
 const char kAutofillCreditCardUploadDescription[] =
-    "Enables a new option to upload credit cards to Google Payments for "
-    "sync to all Chrome devices.";
+    "Enables a new option to upload credit cards to Google Payments for sync "
+    "to all Chrome devices.";
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
@@ -1633,8 +1621,8 @@
 
 const char kForceTextDirectionDescription[] =
     "Explicitly force the per-character directionality of UI text to "
-    "left-to-right (LTR) or right-to-left (RTL) mode, overriding the "
-    "default direction of the character language.";
+    "left-to-right (LTR) or right-to-left (RTL) mode, overriding the default "
+    "direction of the character language.";
 
 const char kForceDirectionLtr[] = "Left-to-right";
 
@@ -1652,8 +1640,7 @@
 const char kEnableGroupedHistoryName[] = "Group history by domain";
 
 const char kEnableGroupedHistoryDescription[] =
-    "Group history by website domain (i.e. google.com) on "
-    "chrome://history.";
+    "Group history by website domain (i.e. google.com) on chrome://history.";
 
 const char kSaveasMenuLabelExperimentName[] =
     "Switch 'Save as' menu labels to 'Download'";
@@ -1677,9 +1664,9 @@
     "New omnibox answers in suggest types";
 
 const char kNewOmniboxAnswerTypesDescription[] =
-    "Enables new types of answers in the omnibox suggest drop-down: "
-    "currency conversions, dictionary definitions, sports scores, "
-    "translations, and when is.";
+    "Enables new types of answers in the omnibox suggest drop-down: currency "
+    "conversions, dictionary definitions, sports scores, translations, and "
+    "when is.";
 
 const char kEnableZeroSuggestRedirectToChromeName[] =
     "Experimental contextual omnibox suggestion";
@@ -1687,14 +1674,14 @@
 const char kEnableZeroSuggestRedirectToChromeDescription[] =
     "Change omnibox contextual suggestions to an experimental source. Note "
     "that this is not an on/off switch for contextual omnibox and it only "
-    "applies to suggestions provided before the user starts typing a URL or "
-    "a search query (i.e. zero suggest).";
+    "applies to suggestions provided before the user starts typing a URL or a "
+    "search query (i.e. zero suggest).";
 
 const char kFillOnAccountSelectName[] = "Fill passwords on account selection";
 
 const char kFillOnAccountSelectDescription[] =
-    "Filling of passwords when an account is explicitly selected by the "
-    "user rather than autofilling credentials on page load.";
+    "Filling of passwords when an account is explicitly selected by the user "
+    "rather than autofilling credentials on page load.";
 
 const char kEnableClearBrowsingDataCountersName[] =
     "Enable Clear browsing data counters.";
@@ -1707,16 +1694,15 @@
 const char kTabsInCbdName[] = "Enable tabs for the Clear Browsing Data dialog.";
 
 const char kTabsInCbdDescription[] =
-    "Enables a basic and an advanced tab for the Clear Browsing Data "
-    "dialog.";
+    "Enables a basic and an advanced tab for the Clear Browsing Data dialog.";
 
 #endif  // defined(OS_ANDROID)
 
 const char kNotificationsNativeFlagName[] = "Enable native notifications.";
 
 const char kNotificationsNativeFlagDescription[] =
-    "Enable support for using the native notification toasts and "
-    "notification center on platforms where these are available.";
+    "Enable support for using the native notification toasts and notification "
+    "center on platforms where these are available.";
 
 #if defined(OS_ANDROID)
 
@@ -1731,21 +1717,21 @@
     "Enable custom layouts for Web Notifications.";
 
 const char kEnableWebNotificationCustomLayoutsDescription[] =
-    "Enable custom layouts for Web Notifications. They will have subtle "
-    "layout improvements that are otherwise not possible.";
+    "Enable custom layouts for Web Notifications. They will have subtle layout "
+    "improvements that are otherwise not possible.";
 
 const char kGoogleProfileInfoName[] = "Google profile name and icon";
 
 const char kGoogleProfileInfoDescription[] =
-    "Enables using Google information to populate the profile name and icon "
-    "in the avatar menu.";
+    "Enables using Google information to populate the profile name and icon in "
+    "the avatar menu.";
 
 const char kOfferStoreUnmaskedWalletCardsName[] =
     "Google Payments card saving checkbox";
 
 const char kOfferStoreUnmaskedWalletCardsDescription[] =
-    "Show the checkbox to offer local saving of a credit card downloaded "
-    "from the server.";
+    "Show the checkbox to offer local saving of a credit card downloaded from "
+    "the server.";
 
 const char kOfflineAutoReloadName[] = "Offline Auto-Reload Mode";
 
@@ -1764,10 +1750,10 @@
 
 const char kShowSavedCopyDescription[] =
     "When a page fails to load, if a stale copy of the page exists in the "
-    "browser cache, a button will be presented to allow the user to load "
-    "that stale copy. The primary enabling choice puts the button in the "
-    "most salient position on the error page; the secondary enabling choice "
-    "puts it secondary to the reload button.";
+    "browser cache, a button will be presented to allow the user to load that "
+    "stale copy. The primary enabling choice puts the button in the most "
+    "salient position on the error page; the secondary enabling choice puts it "
+    "secondary to the reload button.";
 
 const char kEnableShowSavedCopyPrimary[] = "Enable: Primary";
 
@@ -1800,8 +1786,8 @@
 const char kPhysicalKeyboardAutocorrectName[] = "Physical keyboard autocorrect";
 
 const char kPhysicalKeyboardAutocorrectDescription[] =
-    "Enable physical keyboard autocorrect for US keyboard, which can "
-    "provide suggestions as typing on physical keyboard.";
+    "Enable physical keyboard autocorrect for US keyboard, which can provide "
+    "suggestions as typing on physical keyboard.";
 
 const char kVoiceInputName[] = "Voice input on virtual keyboard";
 
@@ -1822,8 +1808,8 @@
 const char kGestureTypingName[] = "Gesture typing for the virtual keyboard.";
 
 const char kGestureTypingDescription[] =
-    "Enable/Disable gesture typing option in the settings page for the "
-    "virtual keyboard.";
+    "Enable/Disable gesture typing option in the settings page for the virtual "
+    "keyboard.";
 
 const char kGestureEditingName[] = "Gesture editing for the virtual keyboard.";
 
@@ -1837,12 +1823,10 @@
 const char kCaptivePortalBypassProxyDescription[] =
     "If proxy is configured, it usually prevents from authorization on "
     "different captive portals. This enables opening captive portal "
-    "authorization dialog in a separate window, which ignores proxy "
-    "settings.";
+    "authorization dialog in a separate window, which ignores proxy settings.";
 
 const char kTouchscreenCalibrationName[] =
-    "Enable/disable touchscreen calibration option in material design "
-    "settings";
+    "Enable/disable touchscreen calibration option in material design settings";
 
 const char kTouchscreenCalibrationDescription[] =
     "If enabled, the user can calibrate the touch screen displays in "
@@ -1868,8 +1852,8 @@
 const char kSimpleCacheBackendName[] = "Simple Cache for HTTP";
 
 const char kSimpleCacheBackendDescription[] =
-    "The Simple Cache for HTTP is a new cache. It relies on the filesystem "
-    "for disk space allocation.";
+    "The Simple Cache for HTTP is a new cache. It relies on the filesystem for "
+    "disk space allocation.";
 
 //  Spelling feedback field trial.
 
@@ -1898,9 +1882,9 @@
 const char kTopDocumentIsolationName[] = "Top document isolation";
 
 const char kTopDocumentIsolationDescription[] =
-    "Highly experimental performance mode where cross-site iframes are kept "
-    "in a separate process from the top document. In this mode, iframes "
-    "from different third-party sites will be allowed to share a process.";
+    "Highly experimental performance mode where cross-site iframes are kept in "
+    "a separate process from the top document. In this mode, iframes from "
+    "different third-party sites will be allowed to share a process.";
 
 //  Cross process guest frames isolation mode
 
@@ -1908,8 +1892,8 @@
     "Cross process frames for guests";
 
 const char kCrossProcessGuestViewIsolationDescription[] =
-    "Highly experimental where guests such as &lt;webview> are implemented "
-    "on the out-of-process iframe infrastructure.";
+    "Highly experimental where guests such as &lt;webview> are implemented on "
+    "the out-of-process iframe infrastructure.";
 
 //  Task Scheduler
 
@@ -1996,8 +1980,7 @@
 const char kSettingsWindowName[] = "Show settings in a window";
 
 const char kSettingsWindowDescription[] =
-    "Settings will be shown in a dedicated window instead of as a browser "
-    "tab.";
+    "Settings will be shown in a dedicated window instead of as a browser tab.";
 
 //  Extension Content Verification
 
@@ -2007,9 +1990,9 @@
 const char kExtensionContentVerificationDescription[] =
     "This flag can be used to turn on verification that the contents of the "
     "files on disk for extensions from the webstore match what they're "
-    "expected to be. This can be used to turn on this feature if it would "
-    "not otherwise have been turned on, but cannot be used to turn it off "
-    "(because this setting can be tampered with by malware).";
+    "expected to be. This can be used to turn on this feature if it would not "
+    "otherwise have been turned on, but cannot be used to turn it off (because "
+    "this setting can be tampered with by malware).";
 
 const char kExtensionContentVerificationBootstrap[] =
     "Bootstrap (get expected hashes, but do not enforce them)";
@@ -2026,8 +2009,8 @@
     "Simulated hardware 'Ok Google' features";
 
 const char kExperimentalHotwordHardwareDescription[] =
-    "Enables an experimental version of 'Ok Google' hotword detection "
-    "features that have a hardware dependency.";
+    "Enables an experimental version of 'Ok Google' hotword detection features "
+    "that have a hardware dependency.";
 
 //  Message center strings
 
@@ -2048,15 +2031,15 @@
     "Cast Streaming hardware video encoding";
 
 const char kCastStreamingHwEncodingDescription[] =
-    "This option enables support in Cast Streaming for encoding video "
-    "streams using platform hardware.";
+    "This option enables support in Cast Streaming for encoding video streams "
+    "using platform hardware.";
 
 const char kAllowInsecureLocalhostName[] =
     "Allow invalid certificates for resources loaded from localhost.";
 
 const char kAllowInsecureLocalhostDescription[] =
-    "Allows requests to localhost over HTTPS even when an invalid "
-    "certificate is presented.";
+    "Allows requests to localhost over HTTPS even when an invalid certificate "
+    "is presented.";
 
 #if defined(OS_WIN) || defined(OS_MACOSX)
 
@@ -2065,10 +2048,10 @@
 const char kAutomaticTabDiscardingName[] = "Automatic tab discarding";
 
 const char kAutomaticTabDiscardingDescription[] =
-    "If enabled, tabs get automatically discarded from memory when the "
-    "system memory is low. Discarded tabs are still visible on the tab "
-    "strip and get reloaded when clicked on. Info about discarded tabs can "
-    "be found at chrome://discards.";
+    "If enabled, tabs get automatically discarded from memory when the system "
+    "memory is low. Discarded tabs are still visible on the tab strip and get "
+    "reloaded when clicked on. Info about discarded tabs can be found at "
+    "chrome://discards.";
 
 #endif  // defined(OS_WIN) || defined(OS_MACOSX)
 
@@ -2131,8 +2114,8 @@
     "Use background loader instead of prerenderer to load pages.";
 
 const char kNewBackgroundLoaderDescription[] =
-    "Use background loader instead of prerenderer to asynchronously "
-    "download pages.";
+    "Use background loader instead of prerenderer to asynchronously download "
+    "pages.";
 
 const char kNtpPopularSitesName[] = "Show popular sites on the New Tab page";
 
@@ -2160,10 +2143,10 @@
 const char kWebPaymentsModifiersName[] = "Enable web payment modifiers";
 
 const char kWebPaymentsModifiersDescription[] =
-    "If the website provides modifiers in the payment request, show the "
-    "custom total for each payment instrument, update the shopping cart "
-    "when instruments are switched, and send modified payment method "
-    "specific data to the payment app.";
+    "If the website provides modifiers in the payment request, show the custom "
+    "total for each payment instrument, update the shopping cart when "
+    "instruments are switched, and send modified payment method specific data "
+    "to the payment app.";
 
 const char kXGEOVisibleNetworksName[] = "Enable XGEO Visible Networks";
 
@@ -2187,8 +2170,8 @@
     "Xperf.";
 
 const char kMergeKeyCharEventsName[] =
-    "Enable or disable merging merging the key event (WM_KEY*) with char "
-    "event (WM_CHAR).";
+    "Enable or disable merging merging the key event (WM_KEY*) with char event "
+    "(WM_CHAR).";
 
 const char kMergeKeyCharEventsDescription[] =
     "If disabled, Chrome will handle WM_KEY* and WM_CHAR separately.";
@@ -2196,8 +2179,8 @@
 const char kUseWinrtMidiApiName[] = "Use Windows Runtime MIDI API";
 
 const char kUseWinrtMidiApiDescription[] =
-    "Use Windows Runtime MIDI API for WebMIDI (effective only on Windows 10 "
-    "or later).";
+    "Use Windows Runtime MIDI API for WebMIDI (effective only on Windows 10 or "
+    "later).";
 
 #endif  // defined(OS_WIN)
 
@@ -2295,8 +2278,8 @@
 const char kEnableScrollAnchoringName[] = "Scroll Anchoring";
 
 const char kEnableScrollAnchoringDescription[] =
-    "Adjusts scroll position to prevent visible jumps when offscreen "
-    "content changes.";
+    "Adjusts scroll position to prevent visible jumps when offscreen content "
+    "changes.";
 
 #if defined(OS_CHROMEOS)
 
@@ -2308,8 +2291,8 @@
 const char kEnableAndroidWallpapersAppName[] = "Android Wallpapers App";
 
 const char kEnableAndroidWallpapersAppDescription[] =
-    "Enables the Android Wallpapers App as the default Wallpaper App on "
-    "Chrome OS.";
+    "Enables the Android Wallpapers App as the default Wallpaper App on Chrome "
+    "OS.";
 
 const char kEnableTouchSupportForScreenMagnifierName[] =
     "Touch support for screen magnifier";
@@ -2348,15 +2331,15 @@
     "Content suggestions category ranker (e.g. on NTP)";
 
 const char kContentSuggestionsCategoryRankerDescription[] =
-    "Set category ranker to order categories of content suggestions (e.g. "
-    "on the NTP).";
+    "Set category ranker to order categories of content suggestions (e.g. on "
+    "the NTP).";
 
 const char kEnableNtpSnippetsVisibilityName[] =
     "Make New Tab Page Snippets more visible.";
 
 const char kEnableNtpSnippetsVisibilityDescription[] =
-    "If enabled, the NTP snippets will become more discoverable with a "
-    "larger portion of the first card above the fold.";
+    "If enabled, the NTP snippets will become more discoverable with a larger "
+    "portion of the first card above the fold.";
 
 const char kEnableContentSuggestionsNewFaviconServerName[] =
     "Get favicons for content suggestions from a new server.";
@@ -2378,13 +2361,6 @@
     "If enabled, missing favicons for NTP tiles get downloaded from Google. "
     "This only applies to tiles that originate from synced history.";
 
-const char kEnableContentSuggestionsCategoriesName[] =
-    "Organize content suggestions by categories.";
-
-const char kEnableContentSuggestionsCategoriesDescription[] =
-    "If enabled, the content suggestions will be grouped by categories or "
-    "topics. Only remote content suggestions are shown when this is enabled.";
-
 const char kEnableContentSuggestionsLargeThumbnailName[] =
     "Large thumbnails layout for content suggestions cards.";
 
@@ -2416,63 +2392,62 @@
     "Show server-side suggestions on the New Tab page";
 
 const char kEnableNtpRemoteSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain server-side suggestions (e.g., "
-    "Articles for you). Furthermore, it allows to override the source used "
-    "to retrieve these server-side suggestions.";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain server-side suggestions (e.g., Articles for you). Furthermore, it "
+    "allows to override the source used to retrieve these server-side "
+    "suggestions.";
 
 const char kEnableNtpRecentOfflineTabSuggestionsName[] =
     "Show recent offline tabs on the New Tab page";
 
 const char kEnableNtpRecentOfflineTabSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain pages that were captured offline "
-    "during browsing (see #offlining-recent-pages)";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain pages that were captured offline during browsing (see "
+    "#offlining-recent-pages)";
 
 const char kEnableNtpAssetDownloadSuggestionsName[] =
     "Show asset downloads on the New Tab page";
 
 const char kEnableNtpAssetDownloadSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain assets (e.g. books, pictures, "
-    "audio) that the user downloaded for later use.";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain assets (e.g. books, pictures, audio) that the user downloaded for "
+    "later use.";
 
 const char kEnableNtpOfflinePageDownloadSuggestionsName[] =
     "Show offline page downloads on the New Tab page";
 
 const char kEnableNtpOfflinePageDownloadSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain pages that the user downloaded for "
-    "later use.";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain pages that the user downloaded for later use.";
 
 const char kEnableNtpBookmarkSuggestionsName[] =
     "Show recently visited bookmarks on the New Tab page";
 
 const char kEnableNtpBookmarkSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain recently visited bookmarks.";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain recently visited bookmarks.";
 
 const char kEnableNtpPhysicalWebPageSuggestionsName[] =
     "Show Physical Web pages on the New Tab page";
 
 const char kEnableNtpPhysicalWebPageSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain pages that are available through "
-    "Physical Web (see #enable-physical-web)";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain pages that are available through Physical Web (see "
+    "#enable-physical-web)";
 
 const char kEnableNtpForeignSessionsSuggestionsName[] =
     "Show recent foreign tabs on the New Tab page";
 
 const char kEnableNtpForeignSessionsSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page (see "
-    "#enable-ntp-snippets) will contain recent foreign tabs.";
+    "If enabled, the list of content suggestions on the New Tab page will "
+    "contain recent foreign tabs.";
 
 const char kEnableNtpSuggestionsNotificationsName[] =
     "Notify about new content suggestions available at the New Tab page";
 
 const char kEnableNtpSuggestionsNotificationsDescription[] =
     "If enabled, notifications will inform about new content suggestions on "
-    "the New Tab page (see #enable-ntp-snippets).";
+    "the New Tab page.";
 
 const char kNtpCondensedLayoutName[] = "Condensed NTP layout";
 
@@ -2497,8 +2472,8 @@
     "Enable offlining of recently visited pages";
 
 const char kOffliningRecentPagesDescription[] =
-    "Enable storing recently visited pages locally for offline use. "
-    "Requires Offline Pages to be enabled.";
+    "Enable storing recently visited pages locally for offline use. Requires "
+    "Offline Pages to be enabled.";
 
 const char kOfflinePagesCtName[] = "Enable Offline Pages CT features.";
 
@@ -2518,8 +2493,7 @@
     "Use expanded autofill credit card popup layout.";
 
 const char kEnableExpandedAutofillCreditCardPopupLayoutDescription[] =
-    "If enabled, displays autofill credit card popup using expanded "
-    "layout.";
+    "If enabled, displays autofill credit card popup using expanded layout.";
 
 #endif  // defined(OS_ANDROID)
 
@@ -2571,23 +2545,22 @@
 const char kFontCacheScalingName[] = "FontCache scaling";
 
 const char kFontCacheScalingDescription[] =
-    "Reuse a cached font in the renderer to serve different sizes of font "
-    "for faster layout.";
+    "Reuse a cached font in the renderer to serve different sizes of font for "
+    "faster layout.";
 
 const char kFramebustingName[] =
     "Framebusting requires same-origin or a user gesture";
 
 const char kFramebustingDescription[] =
-    "Don't permit an iframe to navigate the top level browsing context "
-    "unless they are same-origin or the iframe is processing a user "
-    "gesture.";
+    "Don't permit an iframe to navigate the top level browsing context unless "
+    "they are same-origin or the iframe is processing a user gesture.";
 
 const char kVibrateRequiresUserGestureName[] =
     "Requiring user gesture for the Vibration API";
 
 const char kVibrateRequiresUserGestureDescription[] =
-    "Block the Vibration API if no user gesture has been received on "
-    "the frame or any embedded frame.";
+    "Block the Vibration API if no user gesture has been received on the frame "
+    "or any embedded frame.";
 
 #if defined(OS_ANDROID)
 #if BUILDFLAG(ENABLE_VR)
@@ -2621,21 +2594,19 @@
 const char kEnableAndroidPayIntegrationV1Name[] = "Enable Android Pay v1";
 
 const char kEnableAndroidPayIntegrationV1Description[] =
-    "Enable integration with Android Pay using the first version of the "
-    "API";
+    "Enable integration with Android Pay using the first version of the API";
 
 const char kEnableAndroidPayIntegrationV2Name[] = "Enable Android Pay v2";
 
 const char kEnableAndroidPayIntegrationV2Description[] =
-    "Enable integration with Android Pay using the second version of the "
-    "API";
+    "Enable integration with Android Pay using the second version of the API";
 
 const char kEnableWebPaymentsSingleAppUiSkipName[] =
     "Enable Web Payments single app UI skip";
 
 const char kEnableWebPaymentsSingleAppUiSkipDescription[] =
-    "Enable Web Payments to skip showing its UI if the developer specifies "
-    "a single app.";
+    "Enable Web Payments to skip showing its UI if the developer specifies a "
+    "single app.";
 
 const char kAndroidPaymentAppsName[] = "Android payment apps";
 
@@ -2687,8 +2658,8 @@
     "Lock screen orientation when playing a video fullscreen.";
 
 const char kVideoFullscreenOrientationLockDescription[] =
-    "Lock the screen orientation of the device to match video orientation "
-    "when a video goes fullscreen. Only on phones.";
+    "Lock the screen orientation of the device to match video orientation when "
+    "a video goes fullscreen. Only on phones.";
 
 //  Video rotate-to-fullscreen experiment strings.
 
@@ -2771,8 +2742,8 @@
     "Have consistent omnibox geolocation access.";
 
 const char kEnableConsistentOmniboxGeolocationDescription[] =
-    "Have consistent geolocation access between the omnibox and default "
-    "search engine.";
+    "Have consistent geolocation access between the omnibox and default search "
+    "engine.";
 
 #endif  // defined(OS_ANDROID)
 
@@ -2782,8 +2753,8 @@
 
 const char kMediaRemotingDescription[] =
     "When Casting a tab to a remote device, enabling this turns on an "
-    "optimization that forwards the content bitstream directly to the "
-    "remote device when a video is fullscreened.";
+    "optimization that forwards the content bitstream directly to the remote "
+    "device when a video is fullscreened.";
 
 //  Play Services LSD permission prompt chrome://flags strings
 
@@ -2805,8 +2776,8 @@
 const char kWindows10CustomTitlebarName[] = "Custom-drawn Windows 10 Titlebar";
 
 const char kWindows10CustomTitlebarDescription[] =
-    "If enabled, Chrome will draw the titlebar and caption buttons instead "
-    "of deferring to Windows.";
+    "If enabled, Chrome will draw the titlebar and caption buttons instead of "
+    "deferring to Windows.";
 
 #endif  // defined(OS_WIN)
 
@@ -2825,8 +2796,8 @@
 const char kAiaFetchingName[] = "Intermediate Certificate Fetching";
 
 const char kAiaFetchingDescription[] =
-    "Enable intermediate certificate fetching when a server does not "
-    "provide sufficient certificates to build a chain to a trusted root.";
+    "Enable intermediate certificate fetching when a server does not provide "
+    "sufficient certificates to build a chain to a trusted root.";
 
 #endif  // defined(OS_ANDROID)
 
@@ -2845,8 +2816,8 @@
 const char kEnableDesktopIosPromotionsName[] = "Desktop to iOS promotions.";
 
 const char kEnableDesktopIosPromotionsDescription[] =
-    "Enable Desktop to iOS promotions, and allow users to see them if they "
-    "are eligible.";
+    "Enable Desktop to iOS promotions, and allow users to see them if they are "
+    "eligible.";
 
 #endif  // defined(OS_WIN)
 
@@ -2870,8 +2841,8 @@
 
 const char kOmniboxTailSuggestionsName[] = "Omnibox tail suggestions";
 const char kOmniboxTailSuggestionsDescription[] =
-    "Enable receiving tail suggestions, a type of search suggestion "
-    "based on the last few words in the query, for the Omnibox.";
+    "Enable receiving tail suggestions, a type of search suggestion based on "
+    "the last few words in the query, for the Omnibox.";
 
 const char kPauseBackgroundTabsName[] = "Pause background tabs";
 const char kPauseBackgroundTabsDescription[] =
@@ -2913,8 +2884,7 @@
 #endif  // defined(OS_ANDROID)
 
 const char kDebugShortcutsDescription[] =
-    "Enables additional keyboard shortcuts that are useful for debugging "
-    "Ash.";
+    "Enables additional keyboard shortcuts that are useful for debugging Ash.";
 
 const char kMemoryAblationName[] = "Memory ablation experiment";
 const char kMemoryAblationDescription[] =
@@ -2938,8 +2908,8 @@
     "Experimental Chromecast support for Video Player";
 
 const char kVideoPlayerChromecastSupportDescription[] =
-    "This option enables experimental Chromecast support for Video Player "
-    "app on ChromeOS.";
+    "This option enables experimental Chromecast support for Video Player app "
+    "on ChromeOS.";
 
 const char kNewZipUnpackerName[] = "New ZIP unpacker";
 
@@ -2961,8 +2931,8 @@
 const char kEnableFullscreenAppListName[] = "Enable The Peeking Launcher";
 
 const char kEnableFullscreenAppListDescription[] =
-    "The peeking launcher UI supports touch and provides more space for upcoming "
-    "features.";
+    "The peeking launcher UI supports touch and provides more space for "
+    "upcoming features.";
 
 const char kMemoryPressureThresholdName[] =
     "Memory discard strategy for advanced pressure handling";
@@ -2985,8 +2955,7 @@
 const char kWakeOnPacketsName[] = "Wake On Packets";
 
 const char kWakeOnPacketsDescription[] =
-    "Enables waking the device based on the receipt of some network "
-    "packets.";
+    "Enables waking the device based on the receipt of some network packets.";
 
 const char kQuickUnlockPinName[] = "Quick Unlock (PIN)";
 
@@ -3004,8 +2973,8 @@
 
 const char kQuickUnlockFingerprintDescription[] =
     "Enabling fingerprint quick unlock allows you to setup and use a "
-    "fingerprint to unlock your Chromebook on the lock screen after you "
-    "have signed into your device.";
+    "fingerprint to unlock your Chromebook on the lock screen after you have "
+    "signed into your device.";
 
 const char kExperimentalAccessibilityFeaturesName[] =
     "Experimental accessibility features";
@@ -3051,8 +3020,8 @@
 const char kMtpWriteSupportName[] = "MTP write support";
 
 const char kMtpWriteSupportDescription[] =
-    "MTP write support in File System API (and file manager). In-place "
-    "editing operations are not supported.";
+    "MTP write support in File System API (and file manager). In-place editing "
+    "operations are not supported.";
 
 const char kCrosRegionsModeName[] = "Cros-regions load mode";
 
@@ -3070,8 +3039,8 @@
 
 const char kPrinterProviderSearchAppDescription[] =
     "Enables Chrome Web Store Gallery app for printer drivers. The app "
-    "searches Chrome Web Store for extensions that support printing to a "
-    "USB printer with specific USB ID.";
+    "searches Chrome Web Store for extensions that support printing to a USB "
+    "printer with specific USB ID.";
 
 const char kArcBootCompleted[] = "Load Android apps automatically";
 
@@ -3087,8 +3056,7 @@
     "Emoji, handwriting and voice input on opt-in IME menu";
 
 const char kEnableEhvInputDescription[] =
-    "Enable access to emoji, handwriting and voice input form opt-in IME "
-    "menu.";
+    "Enable access to emoji, handwriting and voice input form opt-in IME menu.";
 
 const char kEnableEncryptionMigrationName[] =
     "Enable encryption migration of user data";
@@ -3282,8 +3250,7 @@
     "Identity consistency between browser and cookie jar";
 
 const char kAccountConsistencyDescription[] =
-    "When enabled, the browser manages signing in and out of Google "
-    "accounts.";
+    "When enabled, the browser manages signing in and out of Google accounts.";
 
 const char kAccountConsistencyChoiceMirror[] = "Mirror";
 const char kAccountConsistencyChoiceDice[] = "Dice";
@@ -3314,8 +3281,8 @@
 const char kDisableNewVirtualKeyboardBehaviorName[] =
     "New window behavior for the accessibility keyboard";
 const char kDisableNewVirtualKeyboardBehaviorDescription[] =
-    "Disable new window behavior for the accessibility keyboard "
-    "in non-sticky mode (do not change work area in non-sticky mode).";
+    "Disable new window behavior for the accessibility keyboard in non-sticky "
+    "mode (do not change work area in non-sticky mode).";
 
 const char kUiDevToolsName[] = "Enable native UI inspection";
 const char kUiDevToolsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index afbb68a..916041f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -907,9 +907,6 @@
 extern const char kEnableContentSuggestionsNewFaviconServerName[];
 extern const char kEnableContentSuggestionsNewFaviconServerDescription[];
 
-extern const char kEnableContentSuggestionsCategoriesName[];
-extern const char kEnableContentSuggestionsCategoriesDescription[];
-
 extern const char kEnableContentSuggestionsLargeThumbnailName[];
 extern const char kEnableContentSuggestionsLargeThumbnailDescription[];
 
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
index e69d467..4a91125 100644
--- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
+++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -157,20 +157,10 @@
 #endif
 }
 
-bool AreLocalCategoriesEnabled() {
-#if defined(OS_ANDROID)
-  return !base::FeatureList::IsEnabled(
-      chrome::android::kContentSuggestionsCategories);
-#else
-  return false;
-#endif
-}
-
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 
 bool IsRecentTabProviderEnabled() {
-  return AreLocalCategoriesEnabled() &&
-         base::FeatureList::IsEnabled(
+  return base::FeatureList::IsEnabled(
              ntp_snippets::kRecentOfflineTabSuggestionsFeature) &&
          base::FeatureList::IsEnabled(
              offline_pages::kOffliningRecentPagesFeature);
@@ -220,8 +210,7 @@
 }
 
 bool IsDownloadsProviderEnabled() {
-  return AreLocalCategoriesEnabled() &&
-         (AreAssetDownloadsEnabled() || AreOfflinePageDownloadsEnabled());
+  return AreAssetDownloadsEnabled() || AreOfflinePageDownloadsEnabled();
 }
 
 void RegisterDownloadsProviderIfEnabled(ContentSuggestionsService* service,
@@ -273,8 +262,7 @@
 #if defined(OS_ANDROID)
 
 bool IsPhysicalWebPageProviderEnabled() {
-  return AreLocalCategoriesEnabled() &&
-         base::FeatureList::IsEnabled(
+  return base::FeatureList::IsEnabled(
              ntp_snippets::kPhysicalWebPageSuggestionsFeature) &&
          base::FeatureList::IsEnabled(chrome::android::kPhysicalWebFeature);
 }
@@ -368,9 +356,8 @@
 }
 
 bool IsForeignSessionsProviderEnabled() {
-  return AreLocalCategoriesEnabled() &&
-         base::FeatureList::IsEnabled(
-             ntp_snippets::kForeignSessionsSuggestionsFeature);
+  return base::FeatureList::IsEnabled(
+      ntp_snippets::kForeignSessionsSuggestionsFeature);
 }
 
 void RegisterForeignSessionsProviderIfEnabled(
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
index 07caa63..3802f70f 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
@@ -49,6 +49,8 @@
 const char kHistogramServiceWorkerLoad[] =
     "PageLoad.Clients.ServiceWorker.DocumentTiming.NavigationToLoadEventFired";
 
+const char kHistogramServiceWorkerParseStartInbox[] =
+    "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart.inbox";
 const char kHistogramServiceWorkerFirstContentfulPaintInbox[] =
     "PageLoad.Clients.ServiceWorker.PaintTiming."
     "NavigationToFirstContentfulPaint.inbox";
@@ -68,6 +70,8 @@
     "PageLoad.Clients.ServiceWorker.DocumentTiming.NavigationToLoadEventFired."
     "inbox";
 
+const char kHistogramServiceWorkerParseStartSearch[] =
+    "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart.search";
 const char kHistogramServiceWorkerFirstContentfulPaintSearch[] =
     "PageLoad.Clients.ServiceWorker.PaintTiming."
     "NavigationToFirstContentfulPaint.search";
@@ -316,6 +320,14 @@
           timing.parse_timing->parse_start, info)) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStart,
                         timing.parse_timing->parse_start.value());
+
+    if (IsInboxSite(info.url)) {
+      PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStartInbox,
+                          timing.parse_timing->parse_start.value());
+    } else if (page_load_metrics::IsGoogleSearchResultUrl(info.url)) {
+      PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStartSearch,
+                          timing.parse_timing->parse_start.value());
+    }
     if (IsForwardBackLoad(transition_)) {
       PAGE_LOAD_HISTOGRAM(
           internal::kHistogramServiceWorkerParseStartForwardBack,
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
index e03159b..a5448c2 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
@@ -27,6 +27,7 @@
 extern const char kHistogramServiceWorkerDomContentLoaded[];
 extern const char kHistogramServiceWorkerLoad[];
 
+extern const char kHistogramServiceWorkerParseStartInbox[];
 extern const char kHistogramServiceWorkerFirstContentfulPaintInbox[];
 extern const char kHistogramServiceWorkerFirstMeaningfulPaintInbox[];
 extern const char
@@ -36,6 +37,7 @@
 extern const char kHistogramServiceWorkerDomContentLoadedInbox[];
 extern const char kHistogramServiceWorkerLoadInbox[];
 
+extern const char kHistogramServiceWorkerParseStartSearch[];
 extern const char kHistogramServiceWorkerFirstContentfulPaintSearch[];
 extern const char kHistogramServiceWorkerFirstMeaningfulPaintSearch[];
 extern const char
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
index 14df9d8d..abd488db 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
@@ -58,6 +58,8 @@
 
   void AssertNoInboxHistogramsLogged() {
     histogram_tester().ExpectTotalCount(
+        internal::kHistogramServiceWorkerParseStartInbox, 0);
+    histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintInbox, 0);
     histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintInbox,
@@ -75,6 +77,8 @@
 
   void AssertNoSearchHistogramsLogged() {
     histogram_tester().ExpectTotalCount(
+        internal::kHistogramServiceWorkerParseStartSearch, 0);
+    histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintSearch, 0);
     histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintSearch,
@@ -258,6 +262,17 @@
   SimulateTimingAndMetadataUpdate(timing, metadata);
 
   histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStartInbox, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStartInbox,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
   histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
@@ -363,6 +378,17 @@
   SimulateTimingAndMetadataUpdate(timing, metadata);
 
   histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStartSearch, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStartSearch,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
   histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
diff --git a/chrome/browser/password_manager/save_password_infobar_delegate_android_unittest.cc b/chrome/browser/password_manager/save_password_infobar_delegate_android_unittest.cc
index a664ede5..311162fd 100644
--- a/chrome/browser/password_manager/save_password_infobar_delegate_android_unittest.cc
+++ b/chrome/browser/password_manager/save_password_infobar_delegate_android_unittest.cc
@@ -110,9 +110,11 @@
 
 std::unique_ptr<MockPasswordFormManager>
 SavePasswordInfoBarDelegateTest::CreateMockFormManager() {
-  return std::unique_ptr<MockPasswordFormManager>(
-      new MockPasswordFormManager(&password_manager_, &client_,
-                                  driver_.AsWeakPtr(), test_form(), &fetcher_));
+  auto manager = base::MakeUnique<MockPasswordFormManager>(
+      &password_manager_, &client_, driver_.AsWeakPtr(), test_form(),
+      &fetcher_);
+  manager->Init(nullptr);
+  return manager;
 }
 
 std::unique_ptr<ConfirmInfoBarDelegate>
diff --git a/chrome/browser/precache/precache_manager_factory.cc b/chrome/browser/precache/precache_manager_factory.cc
index 54d6c736..3ec3cfb7 100644
--- a/chrome/browser/precache/precache_manager_factory.cc
+++ b/chrome/browser/precache/precache_manager_factory.cc
@@ -54,8 +54,6 @@
       new PrecacheDatabase());
   base::FilePath db_path(browser_context->GetPath().Append(
       base::FilePath(FILE_PATH_LITERAL("PrecacheDatabase"))));
-  auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile(
-      Profile::FromBrowserContext(browser_context));
   return new PrecacheManager(
       browser_context,
       ProfileSyncServiceFactory::GetSyncServiceForBrowserContext(
@@ -65,8 +63,6 @@
           ServiceAccessType::IMPLICIT_ACCESS),
       DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
           browser_context),
-      loading_predictor ? loading_predictor->resource_prefetch_predictor()
-                        : nullptr,
       db_path, std::move(precache_database));
 }
 
diff --git a/chrome/browser/predictors/loading_predictor.cc b/chrome/browser/predictors/loading_predictor.cc
index d9b9411..d6c7f9b 100644
--- a/chrome/browser/predictors/loading_predictor.cc
+++ b/chrome/browser/predictors/loading_predictor.cc
@@ -31,6 +31,8 @@
       observer_(nullptr),
       weak_factory_(this) {
   resource_prefetch_predictor_->SetStatsCollector(stats_collector_.get());
+  preconnect_manager_ = base::MakeUnique<PreconnectManager>(
+      GetWeakPtr(), profile_->GetRequestContext());
 }
 
 LoadingPredictor::~LoadingPredictor() = default;
@@ -38,13 +40,31 @@
 void LoadingPredictor::PrepareForPageLoad(const GURL& url, HintOrigin origin) {
   if (active_hints_.find(url) != active_hints_.end())
     return;
-  ResourcePrefetchPredictor::Prediction prediction;
-  if (!resource_prefetch_predictor_->GetPrefetchData(url, &prediction))
-    return;
 
-  // To report hint durations.
-  active_hints_.emplace(url, base::TimeTicks::Now());
-  MaybeAddPrefetch(url, prediction, origin);
+  bool hint_activated = false;
+
+  {
+    ResourcePrefetchPredictor::Prediction prediction;
+    if (resource_prefetch_predictor_->GetPrefetchData(url, &prediction)) {
+      MaybeAddPrefetch(url, prediction.subresource_urls, origin);
+      hint_activated = true;
+    }
+  }
+
+  if (!hint_activated) {
+    PreconnectPrediction prediction;
+    if (resource_prefetch_predictor_->PredictPreconnectOrigins(url,
+                                                               &prediction)) {
+      MaybeAddPreconnect(url, prediction.preconnect_origins,
+                         prediction.preresolve_hosts, origin);
+      hint_activated = true;
+    }
+  }
+
+  if (hint_activated) {
+    // To report hint durations and deduplicate hints to the same url.
+    active_hints_.emplace(url, base::TimeTicks::Now());
+  }
 }
 
 void LoadingPredictor::CancelPageLoadHint(const GURL& url) {
@@ -70,12 +90,12 @@
   for (auto& kv : prefetches_)
     prefetchers.push_back(std::move(kv.second.first));
 
-  // |prefetchers| must be destroyed on the IO thread.
+  // |prefetchers| and |preconnect_manager_| must be destroyed on the IO thread.
   content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(
-          [](std::vector<std::unique_ptr<ResourcePrefetcher>> prefetchers) {},
-          std::move(prefetchers)));
+      base::BindOnce([](std::vector<std::unique_ptr<ResourcePrefetcher>>,
+                        std::unique_ptr<PreconnectManager>) {},
+                     std::move(prefetchers), std::move(preconnect_manager_)));
 }
 
 void LoadingPredictor::OnMainFrameRequest(const URLRequestSummary& summary) {
@@ -126,7 +146,7 @@
 
   const GURL& url = hint_it->first;
   MaybeRemovePrefetch(url);
-
+  MaybeRemovePreconnect(url);
   UMA_HISTOGRAM_TIMES(
       internal::kResourcePrefetchPredictorPrefetchingDurationHistogram,
       base::TimeTicks::Now() - hint_it->second);
@@ -165,10 +185,9 @@
   }
 }
 
-void LoadingPredictor::MaybeAddPrefetch(
-    const GURL& url,
-    const ResourcePrefetchPredictor::Prediction& prediction,
-    HintOrigin origin) {
+void LoadingPredictor::MaybeAddPrefetch(const GURL& url,
+                                        const std::vector<GURL>& urls,
+                                        HintOrigin origin) {
   if (!config_.IsPrefetchingEnabledForOrigin(profile_, origin))
     return;
   std::string host = url.host();
@@ -178,8 +197,7 @@
   auto prefetcher = base::MakeUnique<ResourcePrefetcher>(
       GetWeakPtr(), profile_->GetRequestContext(),
       config_.max_prefetches_inflight_per_navigation,
-      config_.max_prefetches_inflight_per_host_per_navigation, url,
-      prediction.subresource_urls);
+      config_.max_prefetches_inflight_per_host_per_navigation, url, urls);
   // base::Unretained(prefetcher.get()) is fine, as |prefetcher| is always
   // destructed on the IO thread, and the destruction task is posted from the
   // UI thread, after the current task. Since the IO thread is FIFO, then
@@ -240,6 +258,39 @@
   prefetches_.erase(it);
 }
 
+void LoadingPredictor::MaybeAddPreconnect(
+    const GURL& url,
+    const std::vector<GURL>& preconnect_origins,
+    const std::vector<GURL>& preresolve_hosts,
+    HintOrigin origin) {
+  // In case Shutdown() has been already called.
+  if (!preconnect_manager_)
+    return;
+  if (!config_.IsPreconnectEnabledForOrigin(profile_, origin))
+    return;
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&PreconnectManager::Start,
+                     base::Unretained(preconnect_manager_.get()), url,
+                     preconnect_origins, preresolve_hosts));
+}
+
+void LoadingPredictor::MaybeRemovePreconnect(const GURL& url) {
+  // In case Shutdown() has been already called.
+  if (!preconnect_manager_)
+    return;
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&PreconnectManager::Stop,
+                     base::Unretained(preconnect_manager_.get()), url));
+}
+
+void LoadingPredictor::PreconnectFinished(const GURL& url) {
+  NOTIMPLEMENTED();
+}
+
 TestLoadingObserver::~TestLoadingObserver() {
   predictor_->SetObserverForTesting(nullptr);
 }
diff --git a/chrome/browser/predictors/loading_predictor.h b/chrome/browser/predictors/loading_predictor.h
index 2aa3f67..b28cc3c0 100644
--- a/chrome/browser/predictors/loading_predictor.h
+++ b/chrome/browser/predictors/loading_predictor.h
@@ -9,11 +9,13 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/predictors/loading_data_collector.h"
+#include "chrome/browser/predictors/preconnect_manager.h"
 #include "chrome/browser/predictors/resource_prefetch_common.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "chrome/browser/predictors/resource_prefetcher.h"
@@ -41,7 +43,8 @@
 //
 // All methods must be called from the UI thread.
 class LoadingPredictor : public KeyedService,
-                         public ResourcePrefetcher::Delegate {
+                         public ResourcePrefetcher::Delegate,
+                         public PreconnectManager::Delegate {
  public:
   LoadingPredictor(const LoadingPredictorConfig& config, Profile* profile);
   ~LoadingPredictor() override;
@@ -79,6 +82,9 @@
       ResourcePrefetcher* prefetcher,
       std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) override;
 
+  // PreconnectManager::Delegate:
+  void PreconnectFinished(const GURL& url) override;
+
   // Sets the |observer| to be notified when prefetches start and
   // finish. A previously registered observer will be discarded. Call this with
   // a nullptr parameter to de-register the observer.
@@ -92,16 +98,23 @@
       std::map<GURL, base::TimeTicks>::iterator hint_it);
   void CleanupAbandonedHintsAndNavigations(const NavigationID& navigation_id);
 
-  // May start a prefetch for |url| with the data from |prediction|, and a
-  // given hint |origin|. A new prefetch may not start if there is already
-  // one in flight, for instance.
+  // May start a prefetch of |urls| for |url| with a given hint |origin|. A new
+  // prefetch may not start if there is already one in flight, for instance.
   void MaybeAddPrefetch(const GURL& url,
-                        const ResourcePrefetchPredictor::Prediction& prediction,
+                        const std::vector<GURL>& urls,
                         HintOrigin origin);
-
   // If a prefetch exists for |url|, stop it.
   void MaybeRemovePrefetch(const GURL& url);
 
+  // May start a preconnect to |preconnect_origins| and preresolve of
+  // |preresolve_hosts| for |url| with a given hint |origin|.
+  void MaybeAddPreconnect(const GURL& url,
+                          const std::vector<GURL>& preconnect_origins,
+                          const std::vector<GURL>& preresolve_hosts,
+                          HintOrigin origin);
+  // If a preconnect exists for |url|, stop it.
+  void MaybeRemovePreconnect(const GURL& url);
+
   // For testing.
   void set_mock_resource_prefetch_predictor(
       std::unique_ptr<ResourcePrefetchPredictor> predictor) {
@@ -113,6 +126,7 @@
   std::unique_ptr<ResourcePrefetchPredictor> resource_prefetch_predictor_;
   std::unique_ptr<LoadingStatsCollector> stats_collector_;
   std::unique_ptr<LoadingDataCollector> loading_data_collector_;
+  std::unique_ptr<PreconnectManager> preconnect_manager_;
   std::map<GURL, base::TimeTicks> active_hints_;
   // Initial URL.
   std::map<NavigationID, GURL> active_navigations_;
diff --git a/chrome/browser/predictors/loading_test_util.cc b/chrome/browser/predictors/loading_test_util.cc
index ef41145f..6ef83fe 100644
--- a/chrome/browser/predictors/loading_test_util.cc
+++ b/chrome/browser/predictors/loading_test_util.cc
@@ -73,15 +73,6 @@
   redirect->set_consecutive_misses(consecutive_misses);
 }
 
-void InitializePrecacheResource(precache::PrecacheResource* resource,
-                                const std::string& url,
-                                double weight_ratio,
-                                precache::PrecacheResource::Type type) {
-  resource->set_url(url);
-  resource->set_weight_ratio(weight_ratio);
-  resource->set_type(type);
-}
-
 void InitializeOriginStat(OriginStat* origin_stat,
                           const std::string& origin,
                           int number_of_hits,
@@ -99,25 +90,6 @@
   origin_stat->set_accessed_network(accessed_network);
 }
 
-void InitializeExperiment(precache::PrecacheManifest* manifest,
-                          uint32_t experiment_id,
-                          const std::vector<bool>& bitset) {
-  std::string binary_bitset;
-  for (size_t i = 0; i < (bitset.size() + 7) / 8; ++i) {
-    char c = 0;
-    for (size_t j = 0; j < 8; ++j) {
-      if (i * 8 + j < bitset.size() && bitset[i * 8 + j])
-        c |= (1 << j);
-    }
-    binary_bitset.push_back(c);
-  }
-
-  precache::PrecacheResourceSelection prs;
-  prs.set_bitset(binary_bitset);
-  (*manifest->mutable_experiments()
-        ->mutable_resources_by_experiment_group())[experiment_id] = prs;
-}
-
 PrefetchData CreatePrefetchData(const std::string& primary_key,
                                 uint64_t last_visit_time) {
   PrefetchData data;
@@ -134,12 +106,6 @@
   return data;
 }
 
-precache::PrecacheManifest CreateManifestData(int64_t id) {
-  precache::PrecacheManifest manifest;
-  manifest.mutable_id()->set_id(id);
-  return manifest;
-}
-
 OriginData CreateOriginData(const std::string& host, uint64_t last_visit_time) {
   OriginData data;
   data.set_host(host);
@@ -496,37 +462,3 @@
 }
 
 }  // namespace predictors
-
-namespace precache {
-
-std::ostream& operator<<(std::ostream& os, const PrecacheManifest& manifest) {
-  os << "[" << manifest.id().id() << "]" << std::endl;
-  for (const PrecacheResource& resource : manifest.resource())
-    os << "\t\t" << resource << std::endl;
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const PrecacheResource& resource) {
-  return os << "[" << resource.url() << "," << resource.top_host_name() << ","
-            << resource.weight_ratio() << "," << resource.weight() << "]";
-}
-
-bool operator==(const PrecacheManifest& lhs, const PrecacheManifest& rhs) {
-  bool equal = lhs.id().id() == rhs.id().id() &&
-               lhs.resource_size() == rhs.resource_size();
-
-  if (!equal)
-    return false;
-
-  for (int i = 0; i < lhs.resource_size(); ++i)
-    equal = equal && lhs.resource(i) == rhs.resource(i);
-
-  return equal;
-}
-
-bool operator==(const PrecacheResource& lhs, const PrecacheResource& rhs) {
-  return lhs.url() == rhs.url() &&
-         AlmostEqual(lhs.weight_ratio(), rhs.weight_ratio());
-}
-
-}  // namespace precache
diff --git a/chrome/browser/predictors/loading_test_util.h b/chrome/browser/predictors/loading_test_util.h
index 2fe23ac..9c8c1b84 100644
--- a/chrome/browser/predictors/loading_test_util.h
+++ b/chrome/browser/predictors/loading_test_util.h
@@ -53,11 +53,6 @@
                             int number_of_misses,
                             int consecutive_misses);
 
-void InitializePrecacheResource(precache::PrecacheResource* resource,
-                                const std::string& url,
-                                double weight_ratio,
-                                precache::PrecacheResource::Type type);
-
 void InitializeOriginStat(OriginStat* origin_stat,
                           const std::string& origin,
                           int number_of_hits,
@@ -67,15 +62,10 @@
                           bool always_access_network,
                           bool accessed_network);
 
-void InitializeExperiment(precache::PrecacheManifest* manifest,
-                          uint32_t experiment_id,
-                          const std::vector<bool>& bitset);
-
 PrefetchData CreatePrefetchData(const std::string& primary_key,
                                 uint64_t last_visit_time = 0);
 RedirectData CreateRedirectData(const std::string& primary_key,
                                 uint64_t last_visit_time = 0);
-precache::PrecacheManifest CreateManifestData(int64_t id = 0);
 OriginData CreateOriginData(const std::string& host,
                             uint64_t last_visit_time = 0);
 
@@ -208,16 +198,4 @@
 
 }  // namespace predictors
 
-namespace precache {
-
-std::ostream& operator<<(std::ostream& stream,
-                         const PrecacheManifest& manifest);
-std::ostream& operator<<(std::ostream& stream,
-                         const PrecacheResource& resource);
-
-bool operator==(const PrecacheManifest& lhs, const PrecacheManifest& rhs);
-bool operator==(const PrecacheResource& lhs, const PrecacheResource& rhs);
-
-}  // namespace precache
-
 #endif  // CHROME_BROWSER_PREDICTORS_LOADING_TEST_UTIL_H_
diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc
new file mode 100644
index 0000000..98e8df6f
--- /dev/null
+++ b/chrome/browser/predictors/preconnect_manager.cc
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/predictors/preconnect_manager.h"
+
+#include <utility>
+
+#include "content/public/browser/browser_thread.h"
+
+namespace predictors {
+
+PreconnectManager::PreconnectManager(
+    base::WeakPtr<Delegate> delegate,
+    scoped_refptr<net::URLRequestContextGetter> context_getter)
+    : delegate_(std::move(delegate)),
+      context_getter_(std::move(context_getter)) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+PreconnectManager::~PreconnectManager() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+void PreconnectManager::Start(const GURL& url,
+                              const std::vector<GURL>& preconnect_origins,
+                              const std::vector<GURL>& preresolve_hosts) {
+  NOTIMPLEMENTED();
+}
+
+void PreconnectManager::Stop(const GURL& url) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace predictors
diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h
new file mode 100644
index 0000000..55518860
--- /dev/null
+++ b/chrome/browser/predictors/preconnect_manager.h
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREDICTORS_PRECONNECT_MANAGER_H_
+#define CHROME_BROWSER_PREDICTORS_PRECONNECT_MANAGER_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+namespace predictors {
+
+// PreconnectManager is responsible for preresolving and preconnecting to
+// origins based on the input list of URLs.
+//  - The input list of URLs is associated with a main frame url that can be
+//  used for cancelling.
+//  - Limits the total number of preresolves in flight.
+//  - Preresolves an URL before preconnecting to it to have a better control on
+//  number of speculative dns requests in flight.
+//  - When stopped, waits for the pending preresolve requests to finish without
+//  issuing preconnects for them.
+//  - All methods of the class except the constructor must be called on the IO
+//  thread. The constructor must be called on the UI thread.
+class PreconnectManager {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Called when all preresolve jobs for the |url| are finished. Note that
+    // some preconnect jobs can be still in progress, because they are
+    // fire-and-forget.
+    // Is called on the UI thread.
+    virtual void PreconnectFinished(const GURL& url) = 0;
+  };
+
+  PreconnectManager(base::WeakPtr<Delegate> delegate,
+                    scoped_refptr<net::URLRequestContextGetter> context_getter);
+  ~PreconnectManager();
+
+  // Starts preconnect and preresolve jobs keyed by |url|.
+  void Start(const GURL& url,
+             const std::vector<GURL>& preconnect_origins,
+             const std::vector<GURL>& preresolve_hosts);
+  // No additional jobs keyed by the |url| will be queued after this.
+  void Stop(const GURL& url);
+
+ private:
+  base::WeakPtr<Delegate> delegate_;
+  scoped_refptr<net::URLRequestContextGetter> context_getter_;
+};
+
+}  // namespace predictors
+
+#endif  // CHROME_BROWSER_PREDICTORS_PRECONNECT_MANAGER_H_
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc
index 4ac25ba9..06086247 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -23,7 +23,6 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/url_utils.h"
 #include "components/mime_util/mime_util.h"
-#include "components/precache/core/precache_manifest_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
@@ -50,7 +49,6 @@
                                 "application/font-sfnt",
                                 "application/font-ttf"};
 
-const size_t kMaxManifestByteSize = 16 * 1024;
 const size_t kNumSampleHosts = 50;
 const size_t kReportReadinessThreshold = 50;
 const float kMinOriginConfidenceToTriggerPreconnect = 0.75;
@@ -100,12 +98,6 @@
   origin->set_accessed_network(summary.accessed_network);
 }
 
-bool IsManifestTooOld(const precache::PrecacheManifest& manifest) {
-  const base::TimeDelta kMaxManifestAge = base::TimeDelta::FromDays(5);
-  return base::Time::Now() - base::Time::FromDoubleT(manifest.id().id()) >
-         kMaxManifestAge;
-}
-
 // Used to fetch the visit count for a URL from the History database.
 class GetUrlVisitCountTask : public history::HistoryDBTask {
  public:
@@ -161,27 +153,16 @@
     ResourcePrefetchPredictor::PrefetchDataMap* host_resource_data,
     ResourcePrefetchPredictor::RedirectDataMap* url_redirect_data,
     ResourcePrefetchPredictor::RedirectDataMap* host_redirect_data,
-    ResourcePrefetchPredictor::ManifestDataMap* manifest_data,
     ResourcePrefetchPredictor::OriginDataMap* origin_data) {
   url_resource_data->InitializeOnDBThread();
   host_resource_data->InitializeOnDBThread();
   url_redirect_data->InitializeOnDBThread();
   host_redirect_data->InitializeOnDBThread();
-  manifest_data->InitializeOnDBThread();
   origin_data->InitializeOnDBThread();
 }
 
 }  // namespace
 
-namespace internal {
-
-bool ManifestCompare::operator()(const precache::PrecacheManifest& lhs,
-                                 const precache::PrecacheManifest& rhs) const {
-  return lhs.id().id() < rhs.id().id();
-}
-
-}  // namespace internal
-
 PreconnectPrediction::PreconnectPrediction() = default;
 PreconnectPrediction::PreconnectPrediction(
     const PreconnectPrediction& prediction) = default;
@@ -413,8 +394,6 @@
       tables_, tables_->url_redirect_table(), config_.max_urls_to_track);
   auto host_redirect_data = base::MakeUnique<RedirectDataMap>(
       tables_, tables_->host_redirect_table(), config_.max_hosts_to_track);
-  auto manifest_data = base::MakeUnique<ManifestDataMap>(
-      tables_, tables_->manifest_table(), config_.max_hosts_to_track);
   auto origin_data = base::MakeUnique<OriginDataMap>(
       tables_, tables_->origin_table(), config_.max_hosts_to_track);
 
@@ -422,13 +401,12 @@
   // will be passed to the reply task.
   auto task = base::BindOnce(InitializeOnDBThread, url_resource_data.get(),
                              host_resource_data.get(), url_redirect_data.get(),
-                             host_redirect_data.get(), manifest_data.get(),
-                             origin_data.get());
+                             host_redirect_data.get(), origin_data.get());
   auto reply = base::BindOnce(
       &ResourcePrefetchPredictor::CreateCaches, weak_factory_.GetWeakPtr(),
       std::move(url_resource_data), std::move(host_resource_data),
       std::move(url_redirect_data), std::move(host_redirect_data),
-      std::move(manifest_data), std::move(origin_data));
+      std::move(origin_data));
 
   BrowserThread::PostTaskAndReply(BrowserThread::DB, FROM_HERE, std::move(task),
                                   std::move(reply));
@@ -677,33 +655,17 @@
   // Use host data if the URL-based prediction isn't available.
   std::string main_frame_url_host = main_frame_url.host();
   if (GetRedirectEndpoint(main_frame_url_host, *host_redirect_data_,
-                          &redirect_endpoint)) {
-    if (PopulatePrefetcherRequest(redirect_endpoint, *host_resource_data_,
-                                  urls)) {
-      if (prediction) {
-        prediction->is_host = true;
-        prediction->main_frame_key = redirect_endpoint;
-        prediction->is_redirected = (redirect_endpoint != main_frame_url_host);
-      }
-      return true;
+                          &redirect_endpoint) &&
+      PopulatePrefetcherRequest(redirect_endpoint, *host_resource_data_,
+                                urls)) {
+    if (prediction) {
+      prediction->is_host = true;
+      prediction->main_frame_key = redirect_endpoint;
+      prediction->is_redirected = (redirect_endpoint != main_frame_url_host);
     }
-
-    if (config_.is_manifests_enabled) {
-      // Use manifest data for host if available.
-      std::string manifest_host = redirect_endpoint;
-      if (base::StartsWith(manifest_host, "www.", base::CompareCase::SENSITIVE))
-        manifest_host.assign(manifest_host, 4, std::string::npos);
-      if (PopulateFromManifest(manifest_host, urls)) {
-        if (prediction) {
-          prediction->is_host = true;
-          prediction->main_frame_key = redirect_endpoint;
-          prediction->is_redirected =
-              (redirect_endpoint != main_frame_url_host);
-        }
-        return true;
-      }
-    }
+    return true;
   }
+
   return false;
 }
 
@@ -763,66 +725,11 @@
   return has_prefetchable_resource;
 }
 
-bool ResourcePrefetchPredictor::PopulateFromManifest(
-    const std::string& manifest_host,
-    std::vector<GURL>* urls) const {
-  precache::PrecacheManifest manifest;
-  if (!manifest_data_->TryGetData(manifest_host, &manifest))
-    return false;
-
-  if (IsManifestTooOld(manifest))
-    return false;
-
-  // This is roughly in line with the threshold we use for resource confidence.
-  const float kMinWeight = 0.7f;
-
-  // Don't prefetch resource if it has false bit in any of the following
-  // bitsets. All bits assumed to be true if an optional has no value.
-  base::Optional<std::vector<bool>> not_unused =
-      precache::GetResourceBitset(manifest, internal::kUnusedRemovedExperiment);
-  base::Optional<std::vector<bool>> not_versioned = precache::GetResourceBitset(
-      manifest, internal::kVersionedRemovedExperiment);
-  base::Optional<std::vector<bool>> not_no_store = precache::GetResourceBitset(
-      manifest, internal::kNoStoreRemovedExperiment);
-
-  std::vector<const precache::PrecacheResource*> filtered_resources;
-
-  bool has_prefetchable_resource = false;
-  for (int i = 0; i < manifest.resource_size(); ++i) {
-    const precache::PrecacheResource& resource = manifest.resource(i);
-    if (resource.weight_ratio() > kMinWeight &&
-        (!not_unused.has_value() || not_unused.value()[i]) &&
-        (!not_versioned.has_value() || not_versioned.value()[i]) &&
-        (!not_no_store.has_value() || not_no_store.value()[i])) {
-      has_prefetchable_resource = true;
-      if (urls)
-        filtered_resources.push_back(&resource);
-    }
-  }
-
-  if (urls) {
-    std::sort(
-        filtered_resources.begin(), filtered_resources.end(),
-        [](const precache::PrecacheResource* x,
-           const precache::PrecacheResource* y) {
-          return ResourcePrefetchPredictorTables::ComputePrecacheResourceScore(
-                     *x) >
-                 ResourcePrefetchPredictorTables::ComputePrecacheResourceScore(
-                     *y);
-        });
-    for (auto* resource : filtered_resources)
-      urls->emplace_back(resource->url());
-  }
-
-  return has_prefetchable_resource;
-}
-
 void ResourcePrefetchPredictor::CreateCaches(
     std::unique_ptr<PrefetchDataMap> url_resource_data,
     std::unique_ptr<PrefetchDataMap> host_resource_data,
     std::unique_ptr<RedirectDataMap> url_redirect_data,
     std::unique_ptr<RedirectDataMap> host_redirect_data,
-    std::unique_ptr<ManifestDataMap> manifest_data,
     std::unique_ptr<OriginDataMap> origin_data) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(INITIALIZING, initialization_state_);
@@ -831,14 +738,12 @@
   DCHECK(host_resource_data);
   DCHECK(url_redirect_data);
   DCHECK(host_redirect_data);
-  DCHECK(manifest_data);
   DCHECK(origin_data);
 
   url_resource_data_ = std::move(url_resource_data);
   host_resource_data_ = std::move(host_resource_data);
   url_redirect_data_ = std::move(url_redirect_data);
   host_redirect_data_ = std::move(host_redirect_data);
-  manifest_data_ = std::move(manifest_data);
   origin_data_ = std::move(origin_data);
 
   ConnectToHistoryService();
@@ -880,27 +785,23 @@
   host_resource_data_->DeleteAllData();
   url_redirect_data_->DeleteAllData();
   host_redirect_data_->DeleteAllData();
-  manifest_data_->DeleteAllData();
   origin_data_->DeleteAllData();
 }
 
 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) {
   std::vector<std::string> urls_to_delete;
   std::vector<std::string> hosts_to_delete;
-  std::vector<std::string> manifest_hosts_to_delete;
 
   // Transform GURLs to keys for given database.
   for (const auto& it : urls) {
     urls_to_delete.emplace_back(it.url().spec());
     hosts_to_delete.emplace_back(it.url().host());
-    manifest_hosts_to_delete.emplace_back(history::HostForTopHosts(it.url()));
   }
 
   url_resource_data_->DeleteData(urls_to_delete);
   host_resource_data_->DeleteData(hosts_to_delete);
   url_redirect_data_->DeleteData(urls_to_delete);
   host_redirect_data_->DeleteData(hosts_to_delete);
-  manifest_data_->DeleteData(manifest_hosts_to_delete);
   origin_data_->DeleteData(hosts_to_delete);
 }
 
@@ -1277,75 +1178,6 @@
   }
 }
 
-void ResourcePrefetchPredictor::OnManifestFetched(
-    const std::string& host,
-    const precache::PrecacheManifest& manifest) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (initialization_state_ != INITIALIZED)
-    return;
-
-  if (!config_.is_manifests_enabled || IsManifestTooOld(manifest))
-    return;
-
-  // The manifest host has "www." prefix stripped, the manifest host could
-  // correspond to two different hosts in the prefetch database.
-  UpdatePrefetchDataByManifest(host, host_resource_data_.get(), manifest);
-  UpdatePrefetchDataByManifest("www." + host, host_resource_data_.get(),
-                               manifest);
-
-  // The manifest is too large to store.
-  if (host.length() > ResourcePrefetchPredictorTables::kMaxStringLength ||
-      static_cast<uint32_t>(manifest.ByteSize()) > kMaxManifestByteSize) {
-    return;
-  }
-
-  precache::PrecacheManifest data = manifest;
-  precache::RemoveUnknownFields(&data);
-  manifest_data_->UpdateData(host, data);
-}
-
-void ResourcePrefetchPredictor::UpdatePrefetchDataByManifest(
-    const std::string& key,
-    PrefetchDataMap* resource_data,
-    const precache::PrecacheManifest& manifest) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(INITIALIZED, initialization_state_);
-
-  PrefetchData data;
-  bool exists = resource_data->TryGetData(key, &data);
-  if (!exists)
-    return;
-
-  std::map<std::string, int> manifest_index;
-  for (int i = 0; i < manifest.resource_size(); ++i)
-    manifest_index.insert({manifest.resource(i).url(), i});
-
-  base::Optional<std::vector<bool>> not_unused =
-      precache::GetResourceBitset(manifest, internal::kUnusedRemovedExperiment);
-
-  bool was_updated = false;
-  if (not_unused.has_value()) {
-    // Remove unused resources from |data|.
-    auto new_end = std::remove_if(
-        data.mutable_resources()->begin(), data.mutable_resources()->end(),
-        [&](const ResourceData& x) {
-          auto it = manifest_index.find(x.resource_url());
-          if (it == manifest_index.end())
-            return false;
-          return !not_unused.value()[it->second];
-        });
-    was_updated = new_end != data.mutable_resources()->end();
-    data.mutable_resources()->erase(new_end, data.mutable_resources()->end());
-  }
-
-  if (was_updated) {
-    if (data.resources_size() == 0)
-      resource_data->DeleteData({key});
-    else
-      resource_data->UpdateData(key, data);
-  }
-}
-
 void ResourcePrefetchPredictor::ConnectToHistoryService() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(INITIALIZING, initialization_state_);
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h
index d1e6806..2aa0fbf 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -28,7 +28,6 @@
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/precache/content/precache_manager.h"
 #include "content/public/common/resource_type.h"
 #include "url/gurl.h"
 
@@ -49,11 +48,6 @@
 const uint32_t kUnusedRemovedExperiment = 0xf7f77166;
 const uint32_t kNoStoreRemovedExperiment = 0xd90a199a;
 
-struct ManifestCompare {
-  bool operator()(const precache::PrecacheManifest& lhs,
-                  const precache::PrecacheManifest& rhs) const;
-};
-
 struct LastVisitTimeCompare {
   template <typename T>
   bool operator()(const T& lhs, const T& rhs) const {
@@ -99,9 +93,7 @@
 //   it to disk in the DB thread through the ResourcePrefetchPredictorTables. It
 //   initiates resource prefetching using the ResourcePrefetcherManager. Owned
 //   by profile.
-class ResourcePrefetchPredictor
-    : public history::HistoryServiceObserver,
-      public precache::PrecacheManager::Delegate {
+class ResourcePrefetchPredictor : public history::HistoryServiceObserver {
  public:
   // Data collected for origin-based prediction, for a single origin during a
   // page load (see PageRequestSummary).
@@ -194,8 +186,6 @@
       GlowplugKeyValueData<PrefetchData, internal::LastVisitTimeCompare>;
   using RedirectDataMap =
       GlowplugKeyValueData<RedirectData, internal::LastVisitTimeCompare>;
-  using ManifestDataMap = GlowplugKeyValueData<precache::PrecacheManifest,
-                                               internal::ManifestCompare>;
   using OriginDataMap =
       GlowplugKeyValueData<OriginData, internal::LastVisitTimeCompare>;
   using NavigationMap =
@@ -229,10 +219,6 @@
   // number of hits.
   bool IsResourcePrefetchable(const ResourceData& resource) const;
 
-  // precache::PrecacheManager::Delegate:
-  void OnManifestFetched(const std::string& host,
-                         const precache::PrecacheManifest& manifest) override;
-
   // Sets the |observer| to be notified when the resource prefetch predictor
   // data changes. Previously registered observer will be discarded. Call
   // this with nullptr parameter to de-register observer.
@@ -287,12 +273,6 @@
                            NavigationUrlNotInDBAndDBFull);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, RedirectUrlNotInDB);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, RedirectUrlInDB);
-  FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, ManifestHostNotInDB);
-  FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, ManifestHostInDB);
-  FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
-                           ManifestHostNotInDBAndDBFull);
-  FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
-                           ManifestUnusedRemoved);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, OnMainFrameRequest);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, OnMainFrameRedirect);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
@@ -300,7 +280,6 @@
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, GetCorrectPLT);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
                            PopulatePrefetcherRequest);
-  FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, PopulateFromManifest);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, GetRedirectEndpoint);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, GetPrefetchData);
   FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
@@ -349,19 +328,12 @@
                                  const PrefetchDataMap& resource_data,
                                  std::vector<GURL>* urls) const;
 
-  // Returns true iff the manifest table contains PrecacheManifest that can be
-  // used for a |manifest_host| and fills |urls| with resources that need to be
-  // prefetched. |urls| may be nullptr to get the return value only.
-  bool PopulateFromManifest(const std::string& manifest_host,
-                            std::vector<GURL>* urls) const;
-
   // Callback for the task to read the predictor database. Takes ownership of
   // all arguments.
   void CreateCaches(std::unique_ptr<PrefetchDataMap> url_resource_data,
                     std::unique_ptr<PrefetchDataMap> host_resource_data,
                     std::unique_ptr<RedirectDataMap> url_redirect_data,
                     std::unique_ptr<RedirectDataMap> host_redirect_data,
-                    std::unique_ptr<ManifestDataMap> manifest_data,
                     std::unique_ptr<OriginDataMap> origin_data);
 
   // Called during initialization when history is read and the predictor
@@ -411,12 +383,6 @@
   void OnHistoryServiceLoaded(
       history::HistoryService* history_service) override;
 
-  // Updates list of resources in the |resource_data| for the |key| according to
-  // the |manifest|.
-  void UpdatePrefetchDataByManifest(const std::string& key,
-                                    PrefetchDataMap* resource_data,
-                                    const precache::PrecacheManifest& manifest);
-
   // Used to connect to HistoryService or register for service loaded
   // notificatioan.
   void ConnectToHistoryService();
@@ -438,7 +404,6 @@
   std::unique_ptr<PrefetchDataMap> host_resource_data_;
   std::unique_ptr<RedirectDataMap> url_redirect_data_;
   std::unique_ptr<RedirectDataMap> host_redirect_data_;
-  std::unique_ptr<ManifestDataMap> manifest_data_;
   std::unique_ptr<OriginDataMap> origin_data_;
 
   NavigationMap inflight_navigations_;
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
index bfce3282c..0ee56dd 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
@@ -24,7 +24,6 @@
 const char kHostResourceTableName[] = "resource_prefetch_predictor_host";
 const char kHostRedirectTableName[] =
     "resource_prefetch_predictor_host_redirect";
-const char kManifestTableName[] = "resource_prefetch_predictor_manifest";
 const char kOriginTableName[] = "resource_prefetch_predictor_origin";
 
 const char kCreateGlobalMetadataStatementTemplate[] =
@@ -37,26 +36,6 @@
     "proto BLOB, "
     "PRIMARY KEY(key))";
 
-predictors::ResourceData::ResourceType PrecacheResourceTypeToResourceType(
-    precache::PrecacheResource::Type resource_type) {
-  using precache::PrecacheResource;
-  using predictors::ResourceData;
-  switch (resource_type) {
-    case PrecacheResource::RESOURCE_TYPE_IMAGE:
-      return ResourceData::RESOURCE_TYPE_IMAGE;
-    case PrecacheResource::RESOURCE_TYPE_FONT:
-      return ResourceData::RESOURCE_TYPE_FONT_RESOURCE;
-    case PrecacheResource::RESOURCE_TYPE_STYLESHEET:
-      return ResourceData::RESOURCE_TYPE_STYLESHEET;
-    case PrecacheResource::RESOURCE_TYPE_SCRIPT:
-      return ResourceData::RESOURCE_TYPE_SCRIPT;
-    case PrecacheResource::RESOURCE_TYPE_OTHER:
-    case PrecacheResource::RESOURCE_TYPE_UNKNOWN:
-    default:
-      return ResourceData::RESOURCE_TYPE_SUB_RESOURCE;
-  }
-}
-
 int GetResourceTypeMultiplier(
     predictors::ResourceData::ResourceType resource_type) {
   switch (resource_type) {
@@ -144,9 +123,6 @@
       kHostResourceTableName);
   host_redirect_table_ = base::MakeUnique<GlowplugKeyValueTable<RedirectData>>(
       kHostRedirectTableName);
-  manifest_table_ =
-      base::MakeUnique<GlowplugKeyValueTable<precache::PrecacheManifest>>(
-          kManifestTableName);
   origin_table_ =
       base::MakeUnique<GlowplugKeyValueTable<OriginData>>(kOriginTableName);
 }
@@ -187,15 +163,6 @@
 }
 
 // static
-float ResourcePrefetchPredictorTables::ComputePrecacheResourceScore(
-    const precache::PrecacheResource& resource) {
-  int type_multiplier = GetResourceTypeMultiplier(
-      PrecacheResourceTypeToResourceType(resource.type()));
-  // This means a strict ordering, since the weight_ratio is in [0,1).
-  return type_multiplier * 10 + resource.weight_ratio();
-}
-
-// static
 float ResourcePrefetchPredictorTables::ComputeOriginScore(
     const OriginStat& origin) {
   // The ranking is done by considering, in this order:
@@ -250,10 +217,6 @@
 ResourcePrefetchPredictorTables::host_redirect_table() {
   return host_redirect_table_.get();
 }
-GlowplugKeyValueTable<precache::PrecacheManifest>*
-ResourcePrefetchPredictorTables::manifest_table() {
-  return manifest_table_.get();
-}
 GlowplugKeyValueTable<OriginData>*
 ResourcePrefetchPredictorTables::origin_table() {
   return origin_table_.get();
@@ -272,6 +235,8 @@
       "resource_prefetch_predictor_url_metadata";
   static const char kHostMetadataTableName[] =
       "resource_prefetch_predictor_host_metadata";
+  static const char kManifestTableName[] =
+      "resource_prefetch_predictor_manifest";
 
   if (incompatible_version) {
     for (const char* table_name :
@@ -334,7 +299,7 @@
 
   for (const char* table_name :
        {kUrlResourceTableName, kHostResourceTableName, kUrlRedirectTableName,
-        kHostRedirectTableName, kManifestTableName, kOriginTableName}) {
+        kHostRedirectTableName, kOriginTableName}) {
     success = success &&
               (db->DoesTableExist(table_name) ||
                db->Execute(base::StringPrintf(
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.h b/chrome/browser/predictors/resource_prefetch_predictor_tables.h
index 9d92dc83..adb9e6f 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.h
@@ -17,7 +17,6 @@
 #include "chrome/browser/predictors/predictor_table_base.h"
 #include "chrome/browser/predictors/resource_prefetch_common.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.pb.h"
-#include "components/precache/core/proto/precache.pb.h"
 
 namespace tracked_objects {
 class Location;
@@ -34,8 +33,6 @@
 //  - UrlRedirectTable - key: url, value: RedirectData
 //  - HostResourceTable - key: host, value: PrefetchData
 //  - HostRedirectTable - key: host, value: RedirectData
-//  - ManifestTable - key: host with stripped "www." prefix,
-//                    value: precache::PrecacheManifest
 //  - OriginTable - key: host, value: OriginData
 class ResourcePrefetchPredictorTables : public PredictorTableBase {
  public:
@@ -50,7 +47,6 @@
   virtual GlowplugKeyValueTable<RedirectData>* url_redirect_table();
   virtual GlowplugKeyValueTable<PrefetchData>* host_resource_table();
   virtual GlowplugKeyValueTable<RedirectData>* host_redirect_table();
-  virtual GlowplugKeyValueTable<precache::PrecacheManifest>* manifest_table();
   virtual GlowplugKeyValueTable<OriginData>* origin_table();
 
   // Removes the resources with more than |max_consecutive_misses| consecutive
@@ -67,10 +63,6 @@
   // misses from |data|.
   static void TrimRedirects(RedirectData* data, size_t max_consecutive_misses);
 
-  // Computes score of |data|.
-  static float ComputePrecacheResourceScore(
-      const precache::PrecacheResource& data);
-
   // Removes the origins with more than |max_consecutive_misses| consecutive
   // misses from |data|.
   static void TrimOrigins(OriginData* data, size_t max_consecutive_misses);
@@ -99,7 +91,7 @@
 
   // Database version. Always increment it when any change is made to the data
   // schema (including the .proto).
-  static constexpr int kDatabaseVersion = 9;
+  static constexpr int kDatabaseVersion = 10;
 
   // PredictorTableBase:
   void CreateTableIfNonExistent() override;
@@ -113,8 +105,6 @@
   std::unique_ptr<GlowplugKeyValueTable<RedirectData>> url_redirect_table_;
   std::unique_ptr<GlowplugKeyValueTable<PrefetchData>> host_resource_table_;
   std::unique_ptr<GlowplugKeyValueTable<RedirectData>> host_redirect_table_;
-  std::unique_ptr<GlowplugKeyValueTable<precache::PrecacheManifest>>
-      manifest_table_;
   std::unique_ptr<GlowplugKeyValueTable<OriginData>> origin_table_;
 
   DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorTables);
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
index 34fd28f..ae72502 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
@@ -28,7 +28,6 @@
 
   using PrefetchDataMap = std::map<std::string, PrefetchData>;
   using RedirectDataMap = std::map<std::string, RedirectData>;
-  using ManifestDataMap = std::map<std::string, precache::PrecacheManifest>;
   using OriginDataMap = std::map<std::string, OriginData>;
 
   void SetUp() override;
@@ -39,7 +38,6 @@
                   PrefetchDataMap* host_resource_data,
                   RedirectDataMap* url_redirect_data,
                   RedirectDataMap* host_redirect_data,
-                  ManifestDataMap* manifest_data,
                   OriginDataMap* origin_data) const;
 
  protected:
@@ -56,8 +54,8 @@
 
  private:
   // Initializes the tables, |test_url_data_|, |test_host_data_|,
-  // |test_url_redirect_data_|, |test_host_redirect_data_|,
-  // |test_manifest_data_|, and |test_origin_data|.
+  // |test_url_redirect_data_|, |test_host_redirect_data_| and
+  // |test_origin_data|.
   void InitializeSampleData();
 
   // Checks that the input PrefetchData are the same, although the resources
@@ -74,14 +72,6 @@
   void TestRedirectsAreEqual(const std::vector<RedirectStat>& lhs,
                              const std::vector<RedirectStat>& rhs) const;
 
-  // Checks that the input ManifestData are the same, although the resources
-  // can be in different order.
-  void TestManifestDataAreEqual(const ManifestDataMap& lhs,
-                                const ManifestDataMap& rhs) const;
-  void TestManifestResourcesAreEqual(
-      const std::vector<precache::PrecacheResource>& lhs,
-      const std::vector<precache::PrecacheResource>& rhs) const;
-
   void TestOriginDataAreEqual(const OriginDataMap& lhs,
                               const OriginDataMap& rhs) const;
   void TestOriginStatsAreEqual(const std::vector<OriginStat>& lhs,
@@ -89,14 +79,12 @@
 
   void AddKey(PrefetchDataMap* m, const std::string& key) const;
   void AddKey(RedirectDataMap* m, const std::string& key) const;
-  void AddKey(ManifestDataMap* m, const std::string& key) const;
   void AddKey(OriginDataMap* m, const std::string& key) const;
 
   PrefetchDataMap test_url_data_;
   PrefetchDataMap test_host_data_;
   RedirectDataMap test_url_redirect_data_;
   RedirectDataMap test_host_redirect_data_;
-  ManifestDataMap test_manifest_data_;
   OriginDataMap test_origin_data_;
 };
 
@@ -135,18 +123,15 @@
 void ResourcePrefetchPredictorTablesTest::TestGetAllData() {
   PrefetchDataMap actual_url_data, actual_host_data;
   RedirectDataMap actual_url_redirect_data, actual_host_redirect_data;
-  ManifestDataMap actual_manifest_data;
   OriginDataMap actual_origin_data;
 
   GetAllData(&actual_url_data, &actual_host_data, &actual_url_redirect_data,
-             &actual_host_redirect_data, &actual_manifest_data,
-             &actual_origin_data);
+             &actual_host_redirect_data, &actual_origin_data);
 
   TestPrefetchDataAreEqual(test_url_data_, actual_url_data);
   TestPrefetchDataAreEqual(test_host_data_, actual_host_data);
   TestRedirectDataAreEqual(test_url_redirect_data_, actual_url_redirect_data);
   TestRedirectDataAreEqual(test_host_redirect_data_, actual_host_redirect_data);
-  TestManifestDataAreEqual(test_manifest_data_, actual_manifest_data);
   TestOriginDataAreEqual(test_origin_data_, actual_origin_data);
 }
 
@@ -170,11 +155,6 @@
       &GlowplugKeyValueTable<RedirectData>::DeleteData,
       base::Unretained(tables_->host_redirect_table()), hosts_to_delete));
 
-  hosts_to_delete = {"en.wikipedia.org"};
-  tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-      &GlowplugKeyValueTable<precache::PrecacheManifest>::DeleteData,
-      base::Unretained(tables_->manifest_table()), hosts_to_delete));
-
   hosts_to_delete = {"twitter.com"};
   tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
       &GlowplugKeyValueTable<OriginData>::DeleteData,
@@ -182,22 +162,18 @@
 
   PrefetchDataMap actual_url_data, actual_host_data;
   RedirectDataMap actual_url_redirect_data, actual_host_redirect_data;
-  ManifestDataMap actual_manifest_data;
   OriginDataMap actual_origin_data;
 
   GetAllData(&actual_url_data, &actual_host_data, &actual_url_redirect_data,
-             &actual_host_redirect_data, &actual_manifest_data,
-             &actual_origin_data);
+             &actual_host_redirect_data, &actual_origin_data);
 
   PrefetchDataMap expected_url_data, expected_host_data;
   RedirectDataMap expected_url_redirect_data, expected_host_redirect_data;
-  ManifestDataMap expected_manifest_data;
   OriginDataMap expected_origin_data;
   AddKey(&expected_url_data, "http://www.reddit.com");
   AddKey(&expected_host_data, "www.facebook.com");
   AddKey(&expected_url_redirect_data, "http://nyt.com");
   AddKey(&expected_host_redirect_data, "bbc.com");
-  AddKey(&expected_manifest_data, "youtube.com");
   AddKey(&expected_origin_data, "abc.xyz");
 
   TestPrefetchDataAreEqual(expected_url_data, actual_url_data);
@@ -206,7 +182,6 @@
                            actual_url_redirect_data);
   TestRedirectDataAreEqual(expected_host_redirect_data,
                            actual_host_redirect_data);
-  TestManifestDataAreEqual(expected_manifest_data, actual_manifest_data);
   TestOriginDataAreEqual(expected_origin_data, actual_origin_data);
 }
 
@@ -261,15 +236,6 @@
                      base::Unretained(tables_->host_redirect_table()),
                      microsoft.primary_key(), microsoft));
 
-  precache::PrecacheManifest theverge;
-  InitializePrecacheResource(theverge.add_resource(),
-                             "https://www.theverge.com/main.js", 0.7,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-
-  tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-      &GlowplugKeyValueTable<precache::PrecacheManifest>::UpdateData,
-      base::Unretained(tables_->manifest_table()), "theverge.com", theverge));
-
   OriginData twitter = CreateOriginData("twitter.com");
   InitializeOriginStat(twitter.add_origins(), "https://dogs.twitter.com", 10, 1,
                        0, 12., false, true);
@@ -279,15 +245,12 @@
 
   PrefetchDataMap actual_url_data, actual_host_data;
   RedirectDataMap actual_url_redirect_data, actual_host_redirect_data;
-  ManifestDataMap actual_manifest_data;
   OriginDataMap actual_origin_data;
   GetAllData(&actual_url_data, &actual_host_data, &actual_url_redirect_data,
-             &actual_host_redirect_data, &actual_manifest_data,
-             &actual_origin_data);
+             &actual_host_redirect_data, &actual_origin_data);
 
   PrefetchDataMap expected_url_data, expected_host_data;
   RedirectDataMap expected_url_redirect_data, expected_host_redirect_data;
-  ManifestDataMap expected_manifest_data;
   OriginDataMap expected_origin_data;
   AddKey(&expected_url_data, "http://www.reddit.com");
   AddKey(&expected_url_data, "http://www.yahoo.com");
@@ -305,10 +268,6 @@
   expected_host_redirect_data.insert(
       std::make_pair("microsoft.com", microsoft));
 
-  AddKey(&expected_manifest_data, "youtube.com");
-  AddKey(&expected_manifest_data, "en.wikipedia.org");
-  expected_manifest_data.insert(std::make_pair("theverge.com", theverge));
-
   AddKey(&expected_origin_data, "abc.xyz");
   expected_origin_data.insert({"twitter.com", twitter});
 
@@ -318,7 +277,6 @@
                            actual_url_redirect_data);
   TestRedirectDataAreEqual(expected_host_redirect_data,
                            actual_host_redirect_data);
-  TestManifestDataAreEqual(expected_manifest_data, actual_manifest_data);
 }
 
 void ResourcePrefetchPredictorTablesTest::TestDeleteAllData() {
@@ -326,16 +284,13 @@
 
   PrefetchDataMap actual_url_data, actual_host_data;
   RedirectDataMap actual_url_redirect_data, actual_host_redirect_data;
-  ManifestDataMap actual_manifest_data;
   OriginDataMap actual_origin_data;
   GetAllData(&actual_url_data, &actual_host_data, &actual_url_redirect_data,
-             &actual_host_redirect_data, &actual_manifest_data,
-             &actual_origin_data);
+             &actual_host_redirect_data, &actual_origin_data);
   EXPECT_TRUE(actual_url_data.empty());
   EXPECT_TRUE(actual_host_data.empty());
   EXPECT_TRUE(actual_url_redirect_data.empty());
   EXPECT_TRUE(actual_host_redirect_data.empty());
-  EXPECT_TRUE(actual_manifest_data.empty());
 }
 
 void ResourcePrefetchPredictorTablesTest::TestPrefetchDataAreEqual(
@@ -426,48 +381,6 @@
   EXPECT_TRUE(lhs_index.empty());
 }
 
-void ResourcePrefetchPredictorTablesTest::TestManifestDataAreEqual(
-    const ManifestDataMap& lhs,
-    const ManifestDataMap& rhs) const {
-  EXPECT_EQ(lhs.size(), rhs.size());
-
-  for (const auto& m : rhs) {
-    const auto lhs_it = lhs.find(m.first);
-    ASSERT_TRUE(lhs_it != lhs.end()) << m.first;
-    EXPECT_EQ(lhs_it->second.id().id(), m.second.id().id());
-
-    std::vector<precache::PrecacheResource> lhs_resources(
-        lhs_it->second.resource().begin(), lhs_it->second.resource().end());
-    std::vector<precache::PrecacheResource> rhs_resources(
-        m.second.resource().begin(), m.second.resource().end());
-
-    TestManifestResourcesAreEqual(lhs_resources, rhs_resources);
-  }
-}
-
-void ResourcePrefetchPredictorTablesTest::TestManifestResourcesAreEqual(
-    const std::vector<precache::PrecacheResource>& lhs,
-    const std::vector<precache::PrecacheResource>& rhs) const {
-  EXPECT_EQ(lhs.size(), rhs.size());
-
-  std::map<std::string, precache::PrecacheResource> lhs_index;
-  // Repeated resources are not allowed.
-  for (const auto& r : lhs)
-    EXPECT_TRUE(lhs_index.insert(std::make_pair(r.url(), r)).second);
-
-  for (const auto& r : rhs) {
-    auto lhs_it = lhs_index.find(r.url());
-    if (lhs_it != lhs_index.end()) {
-      EXPECT_EQ(r, lhs_it->second);
-      lhs_index.erase(lhs_it);
-    } else {
-      ADD_FAILURE() << r.url();
-    }
-  }
-
-  EXPECT_TRUE(lhs_index.empty());
-}
-
 void ResourcePrefetchPredictorTablesTest::TestOriginDataAreEqual(
     const OriginDataMap& lhs,
     const OriginDataMap& rhs) const {
@@ -532,13 +445,6 @@
   m->insert(*it);
 }
 
-void ResourcePrefetchPredictorTablesTest::AddKey(ManifestDataMap* m,
-                                                 const std::string& key) const {
-  auto it = test_manifest_data_.find(key);
-  ASSERT_TRUE(it != test_manifest_data_.end());
-  m->insert(*it);
-}
-
 void ResourcePrefetchPredictorTablesTest::AddKey(OriginDataMap* m,
                                                  const std::string& key) const {
   auto it = test_origin_data_.find(key);
@@ -559,9 +465,6 @@
   tables_->ExecuteDBTaskOnDBThread(
       base::BindOnce(&GlowplugKeyValueTable<RedirectData>::DeleteAllData,
                      base::Unretained(tables_->host_redirect_table())));
-  tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-      &GlowplugKeyValueTable<precache::PrecacheManifest>::DeleteAllData,
-      base::Unretained(tables_->manifest_table())));
   tables_->ExecuteDBTaskOnDBThread(
       base::BindOnce(&GlowplugKeyValueTable<OriginData>::DeleteAllData,
                      base::Unretained(tables_->origin_table())));
@@ -572,7 +475,6 @@
     PrefetchDataMap* host_resource_data,
     RedirectDataMap* url_redirect_data,
     RedirectDataMap* host_redirect_data,
-    ManifestDataMap* manifest_data,
     OriginDataMap* origin_data) const {
   tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
       &GlowplugKeyValueTable<PrefetchData>::GetAllData,
@@ -586,9 +488,6 @@
   tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
       &GlowplugKeyValueTable<RedirectData>::GetAllData,
       base::Unretained(tables_->host_redirect_table()), host_redirect_data));
-  tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-      &GlowplugKeyValueTable<precache::PrecacheManifest>::GetAllData,
-      base::Unretained(tables_->manifest_table()), manifest_data));
   tables_->ExecuteDBTaskOnDBThread(
       base::BindOnce(&GlowplugKeyValueTable<OriginData>::GetAllData,
                      base::Unretained(tables_->origin_table()), origin_data));
@@ -753,36 +652,6 @@
                        microsoft.primary_key(), microsoft));
   }
 
-  {  // Manifest data.
-    precache::PrecacheManifest wikipedia;
-    InitializePrecacheResource(
-        wikipedia.add_resource(), "https://en.wikipedia.org/script.js", 0.7,
-        precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-    InitializePrecacheResource(wikipedia.add_resource(),
-                               "https://en.wikipedia.org/image.png", 0.3,
-                               precache::PrecacheResource::RESOURCE_TYPE_IMAGE);
-
-    precache::PrecacheManifest youtube;
-    InitializePrecacheResource(youtube.add_resource(),
-                               "https://youtube.com/photo.jpg", 0.5,
-                               precache::PrecacheResource::RESOURCE_TYPE_IMAGE);
-    InitializePrecacheResource(
-        youtube.add_resource(), "https://youtube.com/base.js", 0.2,
-        precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-
-    test_manifest_data_.clear();
-    test_manifest_data_.insert(std::make_pair("en.wikipedia.org", wikipedia));
-    test_manifest_data_.insert(std::make_pair("youtube.com", youtube));
-
-    tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-        &GlowplugKeyValueTable<precache::PrecacheManifest>::UpdateData,
-        base::Unretained(tables_->manifest_table()), "en.wikipedia.org",
-        wikipedia));
-    tables_->ExecuteDBTaskOnDBThread(base::BindOnce(
-        &GlowplugKeyValueTable<precache::PrecacheManifest>::UpdateData,
-        base::Unretained(tables_->manifest_table()), "youtube.com", youtube));
-  }
-
   {  // Origin data.
     OriginData twitter;
     twitter.set_host("twitter.com");
@@ -861,33 +730,6 @@
             compute_score(net::HIGHEST, content::RESOURCE_TYPE_SCRIPT, 42.));
 }
 
-TEST_F(ResourcePrefetchPredictorTablesTest, ComputePrecacheResourceScore) {
-  auto compute_score = [](precache::PrecacheResource::Type resource_type,
-                          float weight_ratio) {
-    precache::PrecacheResource resource;
-    InitializePrecacheResource(&resource, "", weight_ratio, resource_type);
-    return ResourcePrefetchPredictorTables::ComputePrecacheResourceScore(
-        resource);
-  };
-
-  // Stylesheets are the most impotant followed by scripts, fonts and images in
-  // this order.
-  EXPECT_GT(
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET, 0.1),
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_SCRIPT, 0.9));
-  EXPECT_GT(
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_SCRIPT, 0.1),
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_FONT, 0.9));
-  EXPECT_GT(
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_FONT, 0.1),
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_IMAGE, 0.9));
-
-  // If resource types are equal, weight ratio matters.
-  EXPECT_GT(
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_SCRIPT, 0.7),
-      compute_score(precache::PrecacheResource::RESOURCE_TYPE_SCRIPT, 0.6));
-}
-
 TEST_F(ResourcePrefetchPredictorTablesTest, ComputeOriginScore) {
   auto compute_score = [](int hits, int misses, double average_position,
                           bool always_access_network, bool accessed_network) {
@@ -965,15 +807,13 @@
 
   PrefetchDataMap url_data, host_data;
   RedirectDataMap url_redirect_data, host_redirect_data;
-  ManifestDataMap manifest_data;
   OriginDataMap origin_data;
   GetAllData(&url_data, &host_data, &url_redirect_data, &host_redirect_data,
-             &manifest_data, &origin_data);
+             &origin_data);
   EXPECT_TRUE(url_data.empty());
   EXPECT_TRUE(host_data.empty());
   EXPECT_TRUE(url_redirect_data.empty());
   EXPECT_TRUE(host_redirect_data.empty());
-  EXPECT_TRUE(manifest_data.empty());
   EXPECT_TRUE(origin_data.empty());
 }
 
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
index 7038919..979196f11 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
@@ -39,7 +39,6 @@
 using PrefetchDataMap = std::map<std::string, PrefetchData>;
 using RedirectDataMap = std::map<std::string, RedirectData>;
 using OriginDataMap = std::map<std::string, OriginData>;
-using ManifestDataMap = std::map<std::string, precache::PrecacheManifest>;
 
 template <typename T>
 class FakeGlowplugKeyValueTable : public GlowplugKeyValueTable<T> {
@@ -94,10 +93,6 @@
     return &host_redirect_table_;
   }
 
-  GlowplugKeyValueTable<precache::PrecacheManifest>* manifest_table() override {
-    return &manifest_table_;
-  }
-
   GlowplugKeyValueTable<OriginData>* origin_table() override {
     return &origin_table_;
   }
@@ -106,7 +101,6 @@
   FakeGlowplugKeyValueTable<RedirectData> url_redirect_table_;
   FakeGlowplugKeyValueTable<PrefetchData> host_resource_table_;
   FakeGlowplugKeyValueTable<RedirectData> host_redirect_table_;
-  FakeGlowplugKeyValueTable<precache::PrecacheManifest> manifest_table_;
   FakeGlowplugKeyValueTable<OriginData> origin_table_;
 
  protected:
@@ -187,7 +181,6 @@
   PrefetchDataMap test_host_data_;
   RedirectDataMap test_url_redirect_data_;
   RedirectDataMap test_host_redirect_data_;
-  ManifestDataMap test_manifest_data_;
   OriginDataMap test_origin_data_;
 
   MockURLRequestJobFactory url_request_job_factory_;
@@ -235,8 +228,6 @@
             mock_tables_->host_resource_table_.data_);
   EXPECT_EQ(*predictor_->host_redirect_data_->data_cache_,
             mock_tables_->host_redirect_table_.data_);
-  EXPECT_EQ(*predictor_->manifest_data_->data_cache_,
-            mock_tables_->manifest_table_.data_);
   EXPECT_EQ(*predictor_->origin_data_->data_cache_,
             mock_tables_->origin_table_.data_);
   loading_predictor_ = nullptr;
@@ -356,24 +347,6 @@
         std::make_pair(microsoft.primary_key(), microsoft));
   }
 
-  {  // Manifest data.
-    precache::PrecacheManifest google = CreateManifestData(11);
-    InitializePrecacheResource(
-        google.add_resource(), "http://google.com/script.js", 0.5,
-        precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-    InitializePrecacheResource(
-        google.add_resource(), "http://static.google.com/style.css", 0.333,
-        precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-
-    precache::PrecacheManifest facebook = CreateManifestData(12);
-    InitializePrecacheResource(
-        facebook.add_resource(), "http://fb.com/static.css", 0.99,
-        precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-
-    test_manifest_data_.insert(std::make_pair("google.com", google));
-    test_manifest_data_.insert(std::make_pair("facebook.com", facebook));
-  }
-
   {  // Origin data.
     OriginData google = CreateOriginData("google.com", 12);
     InitializeOriginStat(google.add_origins(), "https://static.google.com", 12,
@@ -407,7 +380,6 @@
   EXPECT_TRUE(mock_tables_->url_redirect_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->host_resource_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->host_redirect_table_.data_.empty());
-  EXPECT_TRUE(mock_tables_->manifest_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->origin_table_.data_.empty());
 }
 
@@ -417,7 +389,6 @@
   mock_tables_->url_redirect_table_.data_ = test_url_redirect_data_;
   mock_tables_->host_resource_table_.data_ = test_host_data_;
   mock_tables_->host_redirect_table_.data_ = test_host_redirect_data_;
-  mock_tables_->manifest_table_.data_ = test_manifest_data_;
   mock_tables_->origin_table_.data_ = test_origin_data_;
 
   ResetPredictor();
@@ -929,129 +900,6 @@
             expected_host_redirect_data);
 }
 
-TEST_F(ResourcePrefetchPredictorTest, ManifestHostNotInDB) {
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(),
-                             "http://cdn.google.com/script.js", 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(
-      manifest.add_resource(), "http://cdn.google.com/style.css", 0.75,
-      precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-
-  predictor_->OnManifestFetched("google.com", manifest);
-
-  EXPECT_EQ(mock_tables_->manifest_table_.data_,
-            ManifestDataMap({{"google.com", manifest}}));
-}
-
-TEST_F(ResourcePrefetchPredictorTest, ManifestHostInDB) {
-  mock_tables_->manifest_table_.data_ = test_manifest_data_;
-  ResetPredictor();
-  InitializePredictor();
-
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(),
-                             "http://google.com/image.jpg", 0.1,
-                             precache::PrecacheResource::RESOURCE_TYPE_IMAGE);
-
-  predictor_->OnManifestFetched("google.com", manifest);
-
-  ManifestDataMap expected_data = test_manifest_data_;
-  expected_data["google.com"] = manifest;
-  EXPECT_EQ(mock_tables_->manifest_table_.data_, expected_data);
-}
-
-TEST_F(ResourcePrefetchPredictorTest, ManifestHostNotInDBAndDBFull) {
-  mock_tables_->manifest_table_.data_ = test_manifest_data_;
-  ResetPredictor();
-  InitializePredictor();
-
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(),
-                             "http://en.wikipedia.org/logo.png", 1.0,
-                             precache::PrecacheResource::RESOURCE_TYPE_IMAGE);
-
-  predictor_->OnManifestFetched("en.wikipedia.org", manifest);
-
-  ManifestDataMap expected_data = test_manifest_data_;
-  expected_data.erase("google.com");
-  expected_data["en.wikipedia.org"] = manifest;
-  EXPECT_EQ(mock_tables_->manifest_table_.data_, expected_data);
-}
-
-TEST_F(ResourcePrefetchPredictorTest, ManifestUnknownFieldsRemoved) {
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(),
-                             "http://cdn.google.com/script.js", 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(
-      manifest.add_resource(), "http://cdn.google.com/style.css", 0.75,
-      precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-
-  precache::PrecacheManifest manifest_with_unknown_fields(manifest);
-  manifest_with_unknown_fields.mutable_id()->mutable_unknown_fields()->append(
-      "DATA");
-  manifest_with_unknown_fields.mutable_unknown_fields()->append("DATA");
-  for (auto& resource : *manifest_with_unknown_fields.mutable_resource()) {
-    resource.mutable_unknown_fields()->append("DATA");
-  }
-
-  predictor_->OnManifestFetched("google.com", manifest_with_unknown_fields);
-
-  EXPECT_EQ(mock_tables_->manifest_table_.data_["google.com"].ByteSize(),
-            manifest.ByteSize());
-}
-
-TEST_F(ResourcePrefetchPredictorTest, ManifestTooOld) {
-  base::Time old_time = base::Time::Now() - base::TimeDelta::FromDays(7);
-  precache::PrecacheManifest manifest =
-      CreateManifestData(old_time.ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(),
-                             "http://cdn.google.com/script.js", 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(
-      manifest.add_resource(), "http://cdn.google.com/style.css", 0.75,
-      precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-
-  predictor_->OnManifestFetched("google.com", manifest);
-
-  EXPECT_TRUE(mock_tables_->manifest_table_.data_.empty());
-}
-
-TEST_F(ResourcePrefetchPredictorTest, ManifestUnusedRemoved) {
-  const std::string& script_url = "http://cdn.google.com/script.js";
-  const std::string& style_url = "http://cdn.google.com/style.css";
-  PrefetchData google = CreatePrefetchData("www.google.com");
-  InitializeResourceData(google.add_resources(), script_url,
-                         content::RESOURCE_TYPE_SCRIPT, 10, 0, 1, 2.1,
-                         net::MEDIUM, false, false);
-  InitializeResourceData(google.add_resources(), style_url,
-                         content::RESOURCE_TYPE_SCRIPT, 10, 0, 1, 2.1,
-                         net::MEDIUM, false, false);
-  predictor_->host_resource_data_->UpdateData(google.primary_key(), google);
-
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(), script_url, 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(
-      manifest.add_resource(), style_url, 0.75,
-      precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-  InitializeExperiment(&manifest, internal::kUnusedRemovedExperiment,
-                       {true, false});
-
-  predictor_->OnManifestFetched("google.com", manifest);
-
-  // style_url should be removed.
-  google.mutable_resources()->RemoveLast();
-  EXPECT_EQ(mock_tables_->host_resource_table_.data_,
-            PrefetchDataMap({{google.primary_key(), google}}));
-}
-
 TEST_F(ResourcePrefetchPredictorTest, DeleteUrls) {
   ResetPredictor(false);
   InitializePredictor();
@@ -1109,13 +957,6 @@
                                                 redirect.second);
   }
 
-  ManifestDataMap manifests;
-  manifests.insert({"google.com", CreateManifestData()});
-  manifests.insert({"apple.com", CreateManifestData()});
-  manifests.insert({"en.wikipedia.org", CreateManifestData()});
-  for (const auto& manifest : manifests)
-    predictor_->manifest_data_->UpdateData(manifest.first, manifest.second);
-
   // TODO(alexilin): Add origin data.
 
   history::URLRows rows;
@@ -1132,22 +973,18 @@
   url_redirects.erase("http://www.apple.com/");
   host_redirects.erase("www.google.com");
   host_redirects.erase("www.nike.com");
-  manifests.erase("google.com");
-  manifests.erase("apple.com");
 
   predictor_->DeleteUrls(rows);
   EXPECT_EQ(mock_tables_->url_resource_table_.data_, url_resources);
   EXPECT_EQ(mock_tables_->host_resource_table_.data_, host_resources);
   EXPECT_EQ(mock_tables_->url_redirect_table_.data_, url_redirects);
   EXPECT_EQ(mock_tables_->host_redirect_table_.data_, host_redirects);
-  EXPECT_EQ(mock_tables_->manifest_table_.data_, manifests);
 
   predictor_->DeleteAllUrls();
   EXPECT_TRUE(mock_tables_->url_resource_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->host_resource_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->url_redirect_table_.data_.empty());
   EXPECT_TRUE(mock_tables_->host_redirect_table_.data_.empty());
-  EXPECT_TRUE(mock_tables_->manifest_table_.data_.empty());
 }
 
 TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRequest) {
@@ -1434,61 +1271,6 @@
   EXPECT_TRUE(urls.empty());
 }
 
-TEST_F(ResourcePrefetchPredictorTest, PopulateFromManifest) {
-  // The data that will be used in populating.
-  precache::PrecacheManifest google =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(google.add_resource(),
-                             "https://static.google.com/good.js", 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(google.add_resource(),
-                             "https://static.google.com/versioned_removed", 0.8,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(google.add_resource(),
-                             "https://static.google.com/unused_removed", 0.8,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(google.add_resource(),
-                             "https://static.google.com/no_store", 0.8,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializePrecacheResource(
-      google.add_resource(), "https://static.google.com/good.css", 0.75,
-      precache::PrecacheResource::RESOURCE_TYPE_STYLESHEET);
-  InitializePrecacheResource(google.add_resource(),
-                             "https://static.google.com/low_confidence", 0.6,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  InitializeExperiment(&google, internal::kVersionedRemovedExperiment,
-                       {true, false, true, true, true});
-  InitializeExperiment(&google, internal::kUnusedRemovedExperiment,
-                       {true, true, false, true, true});
-  InitializeExperiment(&google, internal::kNoStoreRemovedExperiment,
-                       {true, true, true, false, true});
-
-  // The data that's too old.
-  base::Time old_time = base::Time::Now() - base::TimeDelta::FromDays(7);
-  precache::PrecacheManifest facebook =
-      CreateManifestData(old_time.ToDoubleT());
-  InitializePrecacheResource(facebook.add_resource(),
-                             "https://static.facebook.com/good", 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-
-  predictor_->manifest_data_->UpdateData("google.com", google);
-  predictor_->manifest_data_->UpdateData("facebook.com", facebook);
-
-  std::vector<GURL> urls;
-  EXPECT_TRUE(predictor_->PopulateFromManifest("google.com", &urls));
-  EXPECT_EQ(urls,
-            std::vector<GURL>({GURL("https://static.google.com/good.css"),
-                               GURL("https://static.google.com/good.js")}));
-
-  urls.clear();
-  EXPECT_FALSE(predictor_->PopulateFromManifest("facebook.com", &urls));
-  EXPECT_TRUE(urls.empty());
-
-  urls.clear();
-  EXPECT_FALSE(predictor_->PopulateFromManifest("404.com", &urls));
-  EXPECT_TRUE(urls.empty());
-}
-
 TEST_F(ResourcePrefetchPredictorTest, GetRedirectEndpoint) {
   // The data to be requested for the confident endpoint.
   RedirectData nyt = CreateRedirectData("http://nyt.com", 1);
@@ -1537,18 +1319,6 @@
   // No prefetch data.
   EXPECT_FALSE(predictor_->GetPrefetchData(main_frame_url, &prediction));
 
-  // Add a manifest associated with the main frame host.
-  const std::string& resource_url = "https://static.google.com/resource";
-  precache::PrecacheManifest manifest =
-      CreateManifestData(base::Time::Now().ToDoubleT());
-  InitializePrecacheResource(manifest.add_resource(), resource_url, 0.9,
-                             precache::PrecacheResource::RESOURCE_TYPE_SCRIPT);
-  predictor_->manifest_data_->UpdateData("google.com", manifest);
-
-  urls.clear();
-  EXPECT_TRUE(predictor_->GetPrefetchData(main_frame_url, &prediction));
-  EXPECT_THAT(urls, UnorderedElementsAre(GURL(resource_url)));
-
   // Add a resource associated with the main frame host.
   PrefetchData google_host = CreatePrefetchData("google.com", 2);
   const std::string script_url = "https://cdn.google.com/script.js";
diff --git a/chrome/browser/predictors/resource_prefetcher.cc b/chrome/browser/predictors/resource_prefetcher.cc
index b5374be..282e8ab 100644
--- a/chrome/browser/predictors/resource_prefetcher.cc
+++ b/chrome/browser/predictors/resource_prefetcher.cc
@@ -57,8 +57,8 @@
     const GURL& main_frame_url,
     const std::vector<GURL>& urls)
     : state_(INITIALIZED),
-      delegate_(delegate),
-      context_getter_(context_getter),
+      delegate_(std::move(delegate)),
+      context_getter_(std::move(context_getter)),
       max_concurrent_requests_(max_concurrent_requests),
       max_concurrent_requests_per_host_(max_concurrent_requests_per_host),
       main_frame_url_(main_frame_url),
diff --git a/chrome/browser/signin/signin_tracker_unittest.cc b/chrome/browser/signin/signin_tracker_unittest.cc
index 772432f..35985a8f 100644
--- a/chrome/browser/signin/signin_tracker_unittest.cc
+++ b/chrome/browser/signin/signin_tracker_unittest.cc
@@ -66,7 +66,7 @@
         static_cast<FakeProfileOAuth2TokenService*>(
             ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get()));
 
-    mock_signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
+    fake_signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
         SigninManagerFactory::GetForProfile(profile_.get()));
 
     tracker_ =
@@ -91,7 +91,7 @@
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<SigninTracker> tracker_;
   std::unique_ptr<TestingProfile> profile_;
-  FakeSigninManagerForTesting* mock_signin_manager_;
+  FakeSigninManagerForTesting* fake_signin_manager_;
   FakeProfileOAuth2TokenService* fake_oauth2_token_service_;
   MockObserver observer_;
 };
@@ -105,7 +105,7 @@
   EXPECT_CALL(observer_, SigninSuccess()).Times(0);
   EXPECT_CALL(observer_, SigninFailed(error));
 
-  mock_signin_manager_->FailSignin(error);
+  fake_signin_manager_->FailSignin(error);
 }
 #endif  // !defined(OS_CHROMEOS)
 
@@ -119,6 +119,21 @@
   std::string email = "user@gmail.com";
   std::string account_id = service->SeedAccountInfo(gaia_id, email);
 
-  mock_signin_manager_->SetAuthenticatedAccountInfo(gaia_id, email);
+  fake_signin_manager_->SetAuthenticatedAccountInfo(gaia_id, email);
   fake_oauth2_token_service_->UpdateCredentials(account_id, "refresh_token");
 }
+
+#if !defined(OS_CHROMEOS)
+TEST_F(SigninTrackerTest, SignInSucceedsWithExistingAccount) {
+  EXPECT_CALL(observer_, SigninSuccess());
+  EXPECT_CALL(observer_, SigninFailed(_)).Times(0);
+
+  AccountTrackerService* service =
+      AccountTrackerServiceFactory::GetForProfile(profile_.get());
+  std::string gaia_id = "gaia_id";
+  std::string email = "user@gmail.com";
+  std::string account_id = service->SeedAccountInfo(gaia_id, email);
+  fake_oauth2_token_service_->UpdateCredentials(account_id, "refresh_token");
+  fake_signin_manager_->SignIn(gaia_id, email, std::string());
+}
+#endif
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc
index 648c7782..b487c95 100644
--- a/chrome/utility/extensions/extensions_handler.cc
+++ b/chrome/utility/extensions/extensions_handler.cc
@@ -234,7 +234,7 @@
       itunes::FindLibraryLocationInPrefXml(itunes_xml_data));
   content::UtilityThread::Get()->Send(
       new ChromeUtilityHostMsg_GotITunesDirectory(library_path));
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcess();
 }
 #endif  // defined(OS_WIN)
 
@@ -246,7 +246,7 @@
   bool result = parser.Parse(iapps::ReadFileAsString(std::move(file)));
   content::UtilityThread::Get()->Send(
       new ChromeUtilityHostMsg_GotITunesLibrary(result, parser.library()));
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcess();
 }
 
 void ExtensionsHandler::OnParsePicasaPMPDatabase(
@@ -272,7 +272,7 @@
   content::UtilityThread::Get()->Send(
       new ChromeUtilityHostMsg_ParsePicasaPMPDatabase_Finished(
           parse_success, reader.albums(), reader.folders()));
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcess();
 }
 
 void ExtensionsHandler::OnIndexPicasaAlbumsContents(
@@ -283,7 +283,7 @@
   content::UtilityThread::Get()->Send(
       new ChromeUtilityHostMsg_IndexPicasaAlbumsContents_Finished(
           indexer.albums_images()));
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcess();
 }
 #endif  // defined(OS_WIN) || defined(OS_MACOSX)
 
diff --git a/chrome/utility/printing_handler.cc b/chrome/utility/printing_handler.cc
index 146ef7a..83366b9a 100644
--- a/chrome/utility/printing_handler.cc
+++ b/chrome/utility/printing_handler.cc
@@ -37,8 +37,8 @@
   return content::UtilityThread::Get()->Send(message);
 }
 
-void ReleaseProcessIfNeeded() {
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+void ReleaseProcess() {
+  content::UtilityThread::Get()->ReleaseProcess();
 }
 
 #if defined(OS_WIN)
@@ -126,7 +126,7 @@
 }
 
 void PrintingHandler::OnRenderPDFPagesToMetafileStop() {
-  ReleaseProcessIfNeeded();
+  ReleaseProcess();
 }
 
 #endif  // defined(OS_WIN)
@@ -145,7 +145,7 @@
   } else {
     Send(new ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed());
   }
-  ReleaseProcessIfNeeded();
+  ReleaseProcess();
 }
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
@@ -317,7 +317,7 @@
     Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed(
         printer_name));
   }
-  ReleaseProcessIfNeeded();
+  ReleaseProcess();
 }
 
 void PrintingHandler::OnGetPrinterSemanticCapsAndDefaults(
@@ -337,7 +337,7 @@
     Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed(
         printer_name));
   }
-  ReleaseProcessIfNeeded();
+  ReleaseProcess();
 }
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
diff --git a/chrome/utility/profile_import_handler.cc b/chrome/utility/profile_import_handler.cc
index 2aa3674a7..a3a03102 100644
--- a/chrome/utility/profile_import_handler.cc
+++ b/chrome/utility/profile_import_handler.cc
@@ -80,5 +80,5 @@
   importer_ = NULL;
   bridge_ = NULL;
   import_thread_.reset();
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcess();
 }
diff --git a/components/browsing_data/core/counters/browsing_data_counter.cc b/components/browsing_data/core/counters/browsing_data_counter.cc
index 243eee8..62befe0 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.cc
+++ b/components/browsing_data/core/counters/browsing_data_counter.cc
@@ -19,8 +19,7 @@
 }
 
 BrowsingDataCounter::BrowsingDataCounter()
-    : initialized_(false),
-      state_(State::IDLE) {}
+    : initialized_(false), use_delay_(true), state_(State::IDLE) {}
 
 BrowsingDataCounter::~BrowsingDataCounter() {}
 
@@ -43,6 +42,7 @@
 void BrowsingDataCounter::InitWithoutPref(base::Time begin_time,
                                           const Callback& callback) {
   DCHECK(!initialized_);
+  use_delay_ = false;
   callback_ = callback;
   clear_browsing_data_tab_ = ClearBrowsingDataTab::ADVANCED;
   begin_time_ = begin_time;
@@ -71,9 +71,14 @@
   state_transitions_.clear();
   state_transitions_.push_back(state_);
 
-  timer_.Start(FROM_HERE,
-               base::TimeDelta::FromMilliseconds(kDelayUntilShowCalculatingMs),
-               this, &BrowsingDataCounter::TransitionToShowCalculating);
+  if (use_delay_) {
+    timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(kDelayUntilShowCalculatingMs), this,
+        &BrowsingDataCounter::TransitionToShowCalculating);
+  } else {
+    state_ = State::READY_TO_REPORT_RESULT;
+  }
   Count();
 }
 
diff --git a/components/browsing_data/core/counters/browsing_data_counter.h b/components/browsing_data/core/counters/browsing_data_counter.h
index bb180b4..3173a6d 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.h
+++ b/components/browsing_data/core/counters/browsing_data_counter.h
@@ -109,6 +109,7 @@
 
   // Can be called instead of |Init()|, to create a counter that doesn't
   // observe pref changes and counts data that was changed since |begin_time|.
+  // This mode doesn't use delayed responses.
   void InitWithoutPref(base::Time begin_time, const Callback& callback);
 
   // Name of the preference associated with this counter.
@@ -178,6 +179,9 @@
   // Whether this class was properly initialized by calling |Init|.
   bool initialized_;
 
+  // Whether to introduce a delayed response to avoid flickering.
+  bool use_delay_;
+
   // State of the counter.
   State state_;
 
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index ad0dba3b..adf31a3 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -44,6 +44,15 @@
          url1.path_piece() == url2.path_piece();
 }
 
+bool HasHomeTile(const NTPTilesVector& tiles) {
+  for (const auto& tile : tiles) {
+    if (tile.source == TileSource::HOMEPAGE) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 MostVisitedSites::MostVisitedSites(
@@ -239,7 +248,7 @@
   }
 
   mv_source_ = TileSource::TOP_SITES;
-  SaveNewTilesAndNotify(std::move(tiles));
+  InitiateNotificationForNewTiles(std::move(tiles));
 }
 
 void MostVisitedSites::OnSuggestionsProfileChanged(
@@ -295,7 +304,7 @@
   }
 
   mv_source_ = TileSource::SUGGESTIONS_SERVICE;
-  SaveNewTilesAndNotify(std::move(tiles));
+  InitiateNotificationForNewTiles(std::move(tiles));
 }
 
 NTPTilesVector MostVisitedSites::CreateWhitelistEntryPointTiles(
@@ -375,16 +384,27 @@
   return popular_sites_tiles;
 }
 
-NTPTilesVector MostVisitedSites::CreatePersonalTilesWithHomeTile(
-    NTPTilesVector tiles) const {
+void MostVisitedSites::OnHomePageTitleDetermined(
+    NTPTilesVector tiles,
+    const base::Optional<base::string16>& title) {
+  if (!title.has_value()) {
+    return;  // If there is no title, the most recent tile was already sent out.
+  }
+  SaveTilesAndNotify(InsertHomeTile(std::move(tiles), title.value()));
+}
+
+NTPTilesVector MostVisitedSites::InsertHomeTile(
+    NTPTilesVector tiles,
+    const base::string16& title) const {
   DCHECK(home_page_client_);
   DCHECK_GT(num_sites_, 0u);
 
-  const GURL& home_page_url = home_page_client_->GetHomepageUrl();
+  const GURL& home_page_url = home_page_client_->GetHomePageUrl();
   NTPTilesVector new_tiles;
   // Add the home tile as first tile.
   NTPTile home_tile;
   home_tile.url = home_page_url;
+  home_tile.title = title;
   home_tile.source = TileSource::HOMEPAGE;
   new_tiles.push_back(std::move(home_tile));
 
@@ -393,6 +413,7 @@
       break;
     }
 
+    // TODO(fhorschig): Introduce a more sophisticated deduplication.
     if (tile.url.host() == home_page_url.host()) {
       continue;
     }
@@ -402,13 +423,22 @@
   return new_tiles;
 }
 
-void MostVisitedSites::SaveNewTilesAndNotify(NTPTilesVector personal_tiles) {
+void MostVisitedSites::InitiateNotificationForNewTiles(
+    NTPTilesVector new_tiles) {
+  if (ShouldAddHomeTile() && !HasHomeTile(new_tiles)) {
+    home_page_client_->QueryHomePageTitle(
+        base::BindOnce(&MostVisitedSites::OnHomePageTitleDetermined,
+                       base::Unretained(this), new_tiles));
+    // Don't wait for the homepage title from history but immediately serve a
+    // copy of new tiles.
+    new_tiles = InsertHomeTile(std::move(new_tiles), base::string16());
+  }
+  SaveTilesAndNotify(std::move(new_tiles));
+}
+
+void MostVisitedSites::SaveTilesAndNotify(NTPTilesVector personal_tiles) {
   std::set<std::string> used_hosts;
   size_t num_actual_tiles = 0u;
-
-  if (ShouldAddHomeTile()) {
-    personal_tiles = CreatePersonalTilesWithHomeTile(std::move(personal_tiles));
-  }
   AddToHostsAndTotalCount(personal_tiles, &used_hosts, &num_actual_tiles);
 
   NTPTilesVector whitelist_tiles =
@@ -487,9 +517,9 @@
          home_page_client_ &&  // No platform-specific implementation - no tile.
          home_page_client_->IsHomePageEnabled() &&
          !home_page_client_->IsNewTabPageUsedAsHomePage() &&
-         !home_page_client_->GetHomepageUrl().is_empty() &&
+         !home_page_client_->GetHomePageUrl().is_empty() &&
          !(top_sites_ &&
-           top_sites_->IsBlacklisted(home_page_client_->GetHomepageUrl()));
+           top_sites_->IsBlacklisted(home_page_client_->GetHomePageUrl()));
 }
 
 void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index 12ed83a09..661623d 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
@@ -95,10 +96,14 @@
   // platform-specific implementation.
   class HomePageClient {
    public:
+    using TitleCallback =
+        base::OnceCallback<void(const base::Optional<base::string16>& title)>;
+
     virtual ~HomePageClient() = default;
     virtual bool IsHomePageEnabled() const = 0;
     virtual bool IsNewTabPageUsedAsHomePage() const = 0;
-    virtual GURL GetHomepageUrl() const = 0;
+    virtual GURL GetHomePageUrl() const = 0;
+    virtual void QueryHomePageTitle(TitleCallback title_callback) = 0;
   };
 
   // Construct a MostVisitedSites instance.
@@ -193,10 +198,14 @@
       const std::set<std::string>& used_hosts,
       size_t num_actual_tiles);
 
+  // Initiates a query for the home page tile if needed and calls
+  // |SaveTilesAndNotify| in the end.
+  void InitiateNotificationForNewTiles(NTPTilesVector new_tiles);
+
   // Takes the personal tiles, creates and merges in whitelist and popular tiles
   // if appropriate, and saves the new tiles. Notifies the observer if the tiles
   // were actually changed.
-  void SaveNewTilesAndNotify(NTPTilesVector personal_tiles);
+  void SaveTilesAndNotify(NTPTilesVector personal_tiles);
 
   void OnPopularSitesDownloaded(bool success);
 
@@ -211,7 +220,11 @@
   // Adds the home page as first tile to |tiles| and returns them as new vector.
   // Drops existing tiles with the same host as the home page and tiles that
   // would exceed the maximum.
-  NTPTilesVector CreatePersonalTilesWithHomeTile(NTPTilesVector tiles) const;
+  NTPTilesVector InsertHomeTile(NTPTilesVector tiles,
+                                const base::string16& title) const;
+
+  void OnHomePageTitleDetermined(NTPTilesVector tiles,
+                                 const base::Optional<base::string16>& title);
 
   // Returns true if there is a valid home page that can be pinned as tile.
   bool ShouldAddHomeTile() const;
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index 5a0d9e18f..a4458b1 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -71,6 +71,7 @@
 using testing::_;
 
 const char kHomePageUrl[] = "http://ho.me/";
+const char kHomePageTitle[] = "Home";
 
 std::string PrintTile(const std::string& title,
                       const std::string& url,
@@ -222,7 +223,11 @@
 
   bool IsNewTabPageUsedAsHomePage() const override { return ntp_is_homepage_; }
 
-  GURL GetHomepageUrl() const override { return home_page_url_; }
+  GURL GetHomePageUrl() const override { return home_page_url_; }
+
+  void QueryHomePageTitle(TitleCallback title_callback) override {
+    std::move(title_callback).Run(home_page_title_);
+  }
 
   void SetHomePageEnabled(bool home_page_enabled) {
     home_page_enabled_ = home_page_enabled;
@@ -234,10 +239,15 @@
 
   void SetHomePageUrl(GURL home_page_url) { home_page_url_ = home_page_url; }
 
+  void SetHomePageTitle(const base::Optional<base::string16>& home_page_title) {
+    home_page_title_ = home_page_title;
+  }
+
  private:
   bool home_page_enabled_;
   bool ntp_is_homepage_;
   GURL home_page_url_;
+  base::Optional<base::string16> home_page_title_;
 };
 
 class MockIconCacher : public IconCacher {
@@ -463,6 +473,34 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_P(MostVisitedSitesTest, ShouldIncludeHomeTileWithUrlBeforeQueryingName) {
+  // Because the query time for the real name might take a while, provide the
+  // home tile with URL as title immediately and update the tiles as soon as the
+  // real title was found.
+  FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+  home_page_client->SetHomePageEnabled(true);
+  home_page_client->SetHomePageTitle(base::UTF8ToUTF16(kHomePageTitle));
+  DisableRemoteSuggestions();
+  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+      .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
+  EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+  EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(false));
+  {
+    testing::Sequence seq;
+    EXPECT_CALL(mock_observer_,
+                OnMostVisitedURLsAvailable(Not(Contains(
+                    MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE)))));
+    EXPECT_CALL(mock_observer_,
+                OnMostVisitedURLsAvailable(Not(Contains(MatchesTile(
+                    kHomePageTitle, kHomePageUrl, TileSource::HOMEPAGE)))));
+  }
+  most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+                                                  /*num_sites=*/3);
+  base::RunLoop().RunUntilIdle();
+}
+
 TEST_P(MostVisitedSitesTest, ShouldUpdateHomePageTileOnHomePageStateChanged) {
   FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
   home_page_client->SetHomePageEnabled(true);
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index d89ec30..3546bfe7 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -726,6 +726,8 @@
         password_manager::PasswordStore* store = client_->GetPasswordStore();
         // May be null in tests.
         if (store) {
+          metrics_util::LogSyncPasswordHashChange(
+              metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA);
           store->SaveSyncPasswordHash(
               provisional_save_manager_->submitted_form()->password_value);
         }
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index a7bc2fe..503a9f9 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -199,6 +199,12 @@
                             SubmittedFormFrame::SUBMITTED_FORM_FRAME_COUNT);
 }
 
+void LogSyncPasswordHashChange(SyncPasswordHashChange event) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "PasswordManager.SyncPasswordHashChange", event,
+      SyncPasswordHashChange::SAVED_SYNC_PASSWORD_CHANGE_COUNT);
+}
+
 }  // namespace metrics_util
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index dc8bdae..a23a9db 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -193,6 +193,13 @@
   SUBMITTED_FORM_FRAME_COUNT
 };
 
+enum class SyncPasswordHashChange {
+  SAVED_ON_CHROME_SIGNIN,
+  SAVED_IN_CONTENT_AREA,
+  CLEARED_ON_CHROME_SIGNOUT,
+  SAVED_SYNC_PASSWORD_CHANGE_COUNT
+};
+
 // Metrics: "PasswordManager.AccessPasswordInSettings"
 enum AccessPasswordInSettingsEvent {
   ACCESS_PASSWORD_VIEWED = 0,
@@ -297,6 +304,9 @@
 // Log a frame of a submitted password form.
 void LogSubmittedFormFrame(SubmittedFormFrame frame);
 
+// Log a save sync password change event.
+void LogSyncPasswordHashChange(SyncPasswordHashChange event);
+
 }  // namespace metrics_util
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store_signin_notifier.cc b/components/password_manager/core/browser/password_store_signin_notifier.cc
index 6b1786a..e874af1 100644
--- a/components/password_manager/core/browser/password_store_signin_notifier.cc
+++ b/components/password_manager/core/browser/password_store_signin_notifier.cc
@@ -4,6 +4,7 @@
 
 #include "components/password_manager/core/browser/password_store_signin_notifier.h"
 
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_store.h"
 
 namespace password_manager {
@@ -13,11 +14,15 @@
 PasswordStoreSigninNotifier::~PasswordStoreSigninNotifier() {}
 
 void PasswordStoreSigninNotifier::NotifySignin(const std::string& password) {
+  metrics_util::LogSyncPasswordHashChange(
+      metrics_util::SyncPasswordHashChange::SAVED_ON_CHROME_SIGNIN);
   if (store_)
     store_->SaveSyncPasswordHash(base::UTF8ToUTF16(password));
 }
 
 void PasswordStoreSigninNotifier::NotifySignedOut() {
+  metrics_util::LogSyncPasswordHashChange(
+      metrics_util::SyncPasswordHashChange::CLEARED_ON_CHROME_SIGNOUT);
   if (store_)
     store_->ClearSyncPasswordHash();
 }
diff --git a/components/precache/content/precache_manager.cc b/components/precache/content/precache_manager.cc
index d9ad0ae9..16283f64 100644
--- a/components/precache/content/precache_manager.cc
+++ b/components/precache/content/precache_manager.cc
@@ -65,14 +65,12 @@
     const history::HistoryService* const history_service,
     const data_reduction_proxy::DataReductionProxySettings*
         data_reduction_proxy_settings,
-    Delegate* delegate,
     const base::FilePath& db_path,
     std::unique_ptr<PrecacheDatabase> precache_database)
     : browser_context_(browser_context),
       sync_service_(sync_service),
       history_service_(history_service),
       data_reduction_proxy_settings_(data_reduction_proxy_settings),
-      delegate_(delegate),
       is_precaching_(false) {
   precache_database_ = std::move(precache_database);
   BrowserThread::PostTask(
@@ -463,13 +461,6 @@
   is_precaching_ = false;
 }
 
-void PrecacheManager::OnManifestFetched(const std::string& host,
-                                        const PrecacheManifest& manifest) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (delegate_)
-    delegate_->OnManifestFetched(host, manifest);
-}
-
 void PrecacheManager::OnHostsReceived(
     const history::TopHostsList& host_counts) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/components/precache/content/precache_manager.h b/components/precache/content/precache_manager.h
index 3f678e9..afd5e6c 100644
--- a/components/precache/content/precache_manager.h
+++ b/components/precache/content/precache_manager.h
@@ -55,7 +55,6 @@
 
 class PrecacheDatabase;
 class PrecacheUnfinishedWork;
-class PrecacheManifest;
 
 extern const char kPrecacheFieldTrialName[];
 
@@ -74,13 +73,6 @@
                         public PrecacheFetcher::PrecacheDelegate,
                         public base::SupportsWeakPtr<PrecacheManager> {
  public:
-  class Delegate {
-   public:
-    // Called when a precache manifest has been successfully fetched and parsed.
-    virtual void OnManifestFetched(const std::string& host,
-                                   const PrecacheManifest& manifest) = 0;
-  };
-
   typedef base::Callback<void(bool)> PrecacheCompletionCallback;
 
   PrecacheManager(content::BrowserContext* browser_context,
@@ -88,7 +80,6 @@
                   const history::HistoryService* history_service,
                   const data_reduction_proxy::DataReductionProxySettings*
                       data_reduction_proxy_settings,
-                  Delegate* delegate,
                   const base::FilePath& db_path,
                   std::unique_ptr<PrecacheDatabase> precache_database);
   ~PrecacheManager() override;
@@ -162,8 +153,6 @@
 
   // From PrecacheFetcher::PrecacheDelegate.
   void OnDone() override;
-  void OnManifestFetched(const std::string& host,
-                         const PrecacheManifest& manifest) override;
 
   // Registers the precache synthetic field trial for users whom the precache
   // task was run recently. |last_precache_time| is the last time precache task
@@ -230,10 +219,6 @@
   const data_reduction_proxy::DataReductionProxySettings* const
       data_reduction_proxy_settings_;
 
-  // The Delegate corresponding to the browser context. Used to notify the
-  // browser about a new available manifest. May be null.
-  Delegate* delegate_;
-
   // The PrecacheFetcher used to precache resources. Should only be used on the
   // UI thread.
   std::unique_ptr<PrecacheFetcher> precache_fetcher_;
diff --git a/components/precache/content/precache_manager_unittest.cc b/components/precache/content/precache_manager_unittest.cc
index 4c39812..67ef094 100644
--- a/components/precache/content/precache_manager_unittest.cc
+++ b/components/precache/content/precache_manager_unittest.cc
@@ -107,12 +107,6 @@
                           const base::Callback<void(int)>& callback));
 };
 
-class MockPrecacheManagerDelegate : public PrecacheManager::Delegate {
- public:
-  MOCK_METHOD2(OnManifestFetched,
-               void(const std::string& host, const PrecacheManifest& manifest));
-};
-
 ACTION_P(ReturnHosts, starting_hosts) {
   arg1.Run(starting_hosts);
 }
@@ -144,14 +138,12 @@
       const history::HistoryService* history_service,
       const data_reduction_proxy::DataReductionProxySettings*
           data_reduction_proxy_settings,
-      Delegate* delegate_,
       const base::FilePath& db_path,
       std::unique_ptr<PrecacheDatabase> precache_database)
       : PrecacheManager(browser_context,
                         sync_service,
                         history_service,
                         data_reduction_proxy_settings,
-                        delegate_,
                         db_path,
                         std::move(precache_database)),
         control_group_(false) {}
@@ -210,8 +202,7 @@
         base::FilePath(FILE_PATH_LITERAL("precache_database")));
     precache_manager_.reset(new PrecacheManagerUnderTest(
         &browser_context_, nullptr /* sync_service */, &history_service_,
-        nullptr /* data_reduction_proxy_settings */,
-        &precache_manager_delegate_, db_path,
+        nullptr /* data_reduction_proxy_settings */, db_path,
         base::WrapUnique(precache_database)));
   }
 
@@ -255,7 +246,6 @@
   net::FakeURLFetcherFactory factory_;
   TestPrecacheCompletionCallback precache_callback_;
   testing::NiceMock<MockHistoryService> history_service_;
-  testing::NiceMock<MockPrecacheManagerDelegate> precache_manager_delegate_;
   base::HistogramTester histograms_;
   net::HttpResponseInfo info_;
   variations::testing::VariationParamsManager variation_params_;
diff --git a/components/precache/core/precache_fetcher.cc b/components/precache/core/precache_fetcher.cc
index a17c6ea..00766ad 100644
--- a/components/precache/core/precache_fetcher.cc
+++ b/components/precache/core/precache_fetcher.cc
@@ -848,7 +848,6 @@
     PrecacheManifest manifest;
 
     if (ParseProtoFromFetchResponse(*source.network_url_fetcher(), &manifest)) {
-      precache_delegate_->OnManifestFetched(source.referrer(), manifest);
       const base::Optional<std::vector<bool>> resource_bitset =
           GetResourceBitset(manifest, experiment_id_);
       const int32_t included_resources_max =
diff --git a/components/precache/core/precache_fetcher.h b/components/precache/core/precache_fetcher.h
index b8c937abb..9868d1c0 100644
--- a/components/precache/core/precache_fetcher.h
+++ b/components/precache/core/precache_fetcher.h
@@ -128,10 +128,6 @@
     // were fetched or not. If the PrecacheFetcher is destroyed before OnDone is
     // called, then precaching will be canceled and OnDone will not be called.
     virtual void OnDone() = 0;
-
-    // Called when a precache manifest has been successfully fetched and parsed.
-    virtual void OnManifestFetched(const std::string& host,
-                                   const PrecacheManifest& manifest) = 0;
   };
 
   // Visible for testing.
diff --git a/components/precache/core/precache_fetcher_unittest.cc b/components/precache/core/precache_fetcher_unittest.cc
index e790f95..178e96b 100644
--- a/components/precache/core/precache_fetcher_unittest.cc
+++ b/components/precache/core/precache_fetcher_unittest.cc
@@ -118,22 +118,12 @@
     on_done_was_called_ = true;
   }
 
-  void OnManifestFetched(const std::string& host,
-                         const PrecacheManifest& manifest) override {
-    hosts.push_back(host);
-  }
-
   bool was_on_done_called() const {
     return on_done_was_called_;
   }
 
-  void clear_manifest_hosts() { hosts.clear(); }
-
-  std::vector<std::string> get_manifest_hosts() const { return hosts; }
-
  private:
   bool on_done_was_called_;
-  std::vector<std::string> hosts;
 };
 
 class MockURLFetcherFactory : public net::URLFetcherFactory {
@@ -583,10 +573,6 @@
   expected_requested_urls.emplace_back(kGoodResourceURL);
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
-  std::vector<std::string> expected_manifest_hosts = {
-      "good-manifest.com", "forced-starting-url.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -673,8 +659,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -749,8 +733,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -818,7 +800,6 @@
       "http://manifest-url-prefix.com/manifest2.com");
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -853,8 +834,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -894,8 +873,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -925,8 +902,6 @@
   expected_requested_urls.emplace_back(kGoodManifestURL);
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -955,8 +930,6 @@
   expected_requested_urls.emplace_back(kGoodManifestURL);
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -993,7 +966,6 @@
   expected_requested_urls.emplace_back(kConfigURL);
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_FALSE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 0);
@@ -1027,7 +999,6 @@
   expected_requested_urls.emplace_back(PRECACHE_CONFIG_SETTINGS_URL);
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -1068,8 +1039,6 @@
   expected_requested_urls.push_back(manifest_url);
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"starting-url.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -1124,8 +1093,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -1185,8 +1152,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -1272,8 +1237,6 @@
   // reason, we are seeing it fetch all but 4 resources. Meh, close enough.
   EXPECT_EQ(1 + 1 + kNumResources - 4, url_callback_.requested_urls().size());
 
-  std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 1);
@@ -1296,7 +1259,6 @@
 
   PrecacheConfigurationSettings config;
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
 
   config.set_top_sites_count(kNumTopHosts);
   factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
@@ -1310,7 +1272,6 @@
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
     expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
-    expected_manifest_hosts.push_back(top_host_url);
   }
 
   for (size_t i = 0; i < kNumTopHosts; ++i) {
@@ -1352,7 +1313,6 @@
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
 
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
@@ -1389,7 +1349,6 @@
   // The config is fetched, but not the invalid manifest URL.
   EXPECT_EQ(1UL, url_callback_.requested_urls().size());
 
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   // manifest.com will have been failed to complete, in this case.
@@ -1430,8 +1389,6 @@
   // The config and manifest are fetched, but not the invalid resource URL.
   EXPECT_EQ(2UL, url_callback_.requested_urls().size());
 
-  std::vector<std::string> expected_manifest_hosts = {"bad-manifest.com"};
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   // bad-manifest.com will have been completed.
@@ -1596,7 +1553,6 @@
   const size_t kNumResources = 5;
 
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
 
   PrecacheConfigurationSettings config;
   config.set_top_sites_count(kNumTopHosts);
@@ -1613,7 +1569,6 @@
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
     expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
-    expected_manifest_hosts.push_back(top_host_url);
   }
 
   // Visit counts and weights are chosen in such a way that resource requests
@@ -1662,7 +1617,6 @@
   }
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -1679,7 +1633,6 @@
   const size_t kNumResources = 5;
 
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
 
   PrecacheConfigurationSettings config;
   config.set_top_sites_count(kNumTopHosts);
@@ -1696,7 +1649,6 @@
   std::vector<std::pair<std::string, float>> resources;
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
-    expected_manifest_hosts.push_back(top_host_url);
     TopHost* top_host = unfinished_work->add_top_host();
     top_host->set_hostname(top_host_url);
     top_host->set_visits(kNumTopHosts - i);
@@ -1760,11 +1712,9 @@
   EXPECT_TRUE(cancelled_work->top_host().empty());
   EXPECT_EQ(kNumTopHosts * kNumResources,
             static_cast<size_t>(cancelled_work->resource().size()));
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_FALSE(precache_delegate_.was_on_done_called());
 
   url_callback_.clear_requested_urls();
-  precache_delegate_.clear_manifest_hosts();
 
   // Continuing with the precache should fetch all resources, as the previous
   // run was cancelled before any finished. They should be fetched in global
@@ -1782,7 +1732,6 @@
     base::RunLoop().RunUntilIdle();
   }
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectBucketCount("Precache.Fetch.MinWeight",
@@ -1795,7 +1744,6 @@
   const size_t kNumResources = 5;
 
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
 
   PrecacheConfigurationSettings config;
   config.set_total_resources_count(2);
@@ -1814,8 +1762,6 @@
 
   expected_requested_urls.emplace_back(kManifestURLPrefix +
                                        top_host->hostname());
-  expected_manifest_hosts.push_back(top_host->hostname());
-
   PrecacheManifest manifest;
   for (size_t i = 0; i < kNumResources; ++i) {
     const float weight = 1 - static_cast<float>(i) / kNumResources;
@@ -1846,7 +1792,6 @@
   }
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   const float expected_min_weight =
@@ -1861,7 +1806,6 @@
   const size_t kNumResources = 5;
 
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
 
   PrecacheConfigurationSettings config;
   config.set_min_weight(3);
@@ -1880,7 +1824,6 @@
 
   expected_requested_urls.emplace_back(kManifestURLPrefix +
                                        top_host->hostname());
-  expected_manifest_hosts.push_back(top_host->hostname());
 
   PrecacheManifest manifest;
   for (size_t i = 0; i < kNumResources; ++i) {
@@ -1911,7 +1854,6 @@
   }
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -1925,7 +1867,6 @@
 
   PrecacheConfigurationSettings config;
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
   std::unique_ptr<PrecacheUnfinishedWork> cancelled_work;
 
   config.set_top_sites_count(kNumTopHosts);
@@ -1940,7 +1881,6 @@
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
     expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
-    expected_manifest_hosts.push_back(top_host_url);
   }
 
   int num_resources = 0;
@@ -2000,14 +1940,12 @@
             static_cast<size_t>(cancelled_work->resource().size()));
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_FALSE(precache_delegate_.was_on_done_called());
 
   // Continuing with the precache should fetch all resources, as the previous
   // run was cancelled before any finished.
   expected_requested_urls.clear();
   url_callback_.clear_requested_urls();
-  precache_delegate_.clear_manifest_hosts();
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     for (size_t j = 0; j < kNumResources; ++j) {
       expected_requested_urls.emplace_back(
@@ -2024,7 +1962,6 @@
     base::RunLoop().RunUntilIdle();
   }
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 }
 
@@ -2043,14 +1980,12 @@
   factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
                            net::HTTP_OK, net::URLRequestStatus::SUCCESS);
   std::vector<GURL> expected_requested_urls;
-  std::vector<std::string> expected_manifest_hosts;
   expected_requested_urls.emplace_back(kConfigURL);
 
   for (size_t i = 0; i < kNumTopHosts; ++i) {
     const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
     expected_requested_urls.emplace_back(std::string(kManifestURLPrefix) +
                                          top_host_url);
-    expected_manifest_hosts.push_back(top_host_url);
   }
 
   for (size_t i = 0; i < kNumTopHosts; ++i) {
@@ -2088,7 +2023,6 @@
   }
 
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   EXPECT_EQ(0, unfinished_work->top_host_size());
@@ -2102,7 +2036,6 @@
   // any resources.
   expected_requested_urls.clear();
   url_callback_.clear_requested_urls();
-  precache_delegate_.clear_manifest_hosts();
   {
     PrecacheFetcher precache_fetcher(
         request_context_.get(), GURL(), std::string(),
@@ -2114,7 +2047,6 @@
     EXPECT_EQ(0U, precache_fetcher.quota_.remaining());
   }
   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-  EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
   EXPECT_TRUE(precache_delegate_.was_on_done_called());
 
   histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 2);
diff --git a/components/signin/core/browser/android/BUILD.gn b/components/signin/core/browser/android/BUILD.gn
index d9dda3e..96540483 100644
--- a/components/signin/core/browser/android/BUILD.gn
+++ b/components/signin/core/browser/android/BUILD.gn
@@ -22,6 +22,7 @@
   java_files = [
     "java/src/org/chromium/components/signin/AccountManagerDelegate.java",
     "java/src/org/chromium/components/signin/AccountManagerHelper.java",
+    "java/src/org/chromium/components/signin/AccountsChangeObserver.java",
     "java/src/org/chromium/components/signin/AuthException.java",
     "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java",
     "java/src/org/chromium/components/signin/ChromeSigninController.java",
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
index 004d68c..15666713 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
@@ -8,6 +8,7 @@
 import android.accounts.AuthenticatorDescription;
 import android.app.Activity;
 import android.support.annotation.AnyThread;
+import android.support.annotation.MainThread;
 import android.support.annotation.Nullable;
 import android.support.annotation.WorkerThread;
 
@@ -19,6 +20,20 @@
  */
 public interface AccountManagerDelegate {
     /**
+     * Adds an observer to get notified about accounts changes.
+     * @param observer the observer to add.
+     */
+    @MainThread
+    void addObserver(AccountsChangeObserver observer);
+
+    /**
+     * Removes an observer that was previously added using {@link #addObserver}.
+     * @param observer the observer to remove.
+     */
+    @MainThread
+    void removeObserver(AccountsChangeObserver observer);
+
+    /**
      * Get all the accounts for a given {@code type}.
      * This is deprecated an will be removed soon.
      */
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
index 2b08786c..9692855 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
@@ -73,6 +73,26 @@
     }
 
     /**
+     * Adds an observer to receive accounts change notifications.
+     * @param observer the observer to add.
+     */
+    @MainThread
+    public void addObserver(AccountsChangeObserver observer) {
+        ThreadUtils.assertOnUiThread();
+        mDelegate.addObserver(observer);
+    }
+
+    /**
+     * Removes an observer that was previously added using {@link #addObserver}.
+     * @param observer the observer to remove.
+     */
+    @MainThread
+    public void removeObserver(AccountsChangeObserver observer) {
+        ThreadUtils.assertOnUiThread();
+        mDelegate.removeObserver(observer);
+    }
+
+    /**
      * @param delegate the account manager to use as a backend service
      */
     private AccountManagerHelper(AccountManagerDelegate delegate) {
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountsChangeObserver.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountsChangeObserver.java
new file mode 100644
index 0000000..370403a
--- /dev/null
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountsChangeObserver.java
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.signin;
+
+import android.support.annotation.MainThread;
+
+/**
+ * Observer that receives account change notifications. Use {@link AccountManagerHelper#addObserver}
+ * and {@link AccountManagerHelper#removeObserver} to update registrations.
+ */
+public interface AccountsChangeObserver {
+    /**
+     * Called on every change to the accounts or to the error condition that occurred while getting
+     * accounts.
+     */
+    @MainThread
+    void onAccountsChanged();
+}
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
index f1231936..9bc930ab 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -13,6 +13,10 @@
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
 import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -27,6 +31,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.library_loader.LibraryLoader;
@@ -42,10 +47,33 @@
 @MainDex
 public class SystemAccountManagerDelegate implements AccountManagerDelegate {
     private final AccountManager mAccountManager;
+    private final ObserverList<AccountsChangeObserver> mObservers = new ObserverList<>();
+
     private static final String TAG = "Auth";
 
     public SystemAccountManagerDelegate() {
         mAccountManager = AccountManager.get(ContextUtils.getApplicationContext());
+
+        BroadcastReceiver accountsChangedBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(final Context context, final Intent intent) {
+                fireOnAccountsChangedNotification();
+            }
+        };
+        IntentFilter accountsChangedIntentFilter = new IntentFilter();
+        accountsChangedIntentFilter.addAction(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
+        ContextUtils.getApplicationContext().registerReceiver(
+                accountsChangedBroadcastReceiver, accountsChangedIntentFilter);
+    }
+
+    @Override
+    public void addObserver(AccountsChangeObserver observer) {
+        mObservers.addObserver(observer);
+    }
+
+    @Override
+    public void removeObserver(AccountsChangeObserver observer) {
+        mObservers.removeObserver(observer);
     }
 
     @Override
@@ -57,6 +85,10 @@
         Account[] accounts = mAccountManager.getAccountsByType(type);
         long elapsed = SystemClock.elapsedRealtime() - now;
         recordElapsedTimeHistogram("Signin.AndroidGetAccountsTime_AccountManager", elapsed);
+        if (ThreadUtils.runningOnUiThread()) {
+            recordElapsedTimeHistogram(
+                    "Signin.AndroidGetAccountsTimeUiThread_AccountManager", elapsed);
+        }
         return accounts;
     }
 
@@ -182,4 +214,10 @@
                        "android.permission.MANAGE_ACCOUNTS", Process.myPid(), Process.myUid())
                 == PackageManager.PERMISSION_GRANTED;
     }
+
+    private void fireOnAccountsChangedNotification() {
+        for (AccountsChangeObserver observer : mObservers) {
+            observer.onAccountsChanged();
+        }
+    }
 }
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
index d61073e84..804d3b4 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
@@ -13,11 +13,13 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
+import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.AccountManagerDelegateException;
 import org.chromium.components.signin.AccountManagerHelper;
+import org.chromium.components.signin.AccountsChangeObserver;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -44,6 +46,7 @@
 
     private final Context mContext;
     private final Set<AccountHolder> mAccounts = new HashSet<>();
+    private final ObserverList<AccountsChangeObserver> mObservers = new ObserverList<>();
 
     @VisibleForTesting
     public FakeAccountManagerDelegate(Context context, Account... accounts) {
@@ -56,6 +59,16 @@
     }
 
     @Override
+    public void addObserver(AccountsChangeObserver observer) {
+        mObservers.addObserver(observer);
+    }
+
+    @Override
+    public void removeObserver(AccountsChangeObserver observer) {
+        mObservers.removeObserver(observer);
+    }
+
+    @Override
     public Account[] getAccountsByType(String type) {
         if (!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(type)) {
             throw new IllegalArgumentException("Invalid account type: " + type);
@@ -90,6 +103,9 @@
     public void addAccountHolderExplicitly(AccountHolder accountHolder) {
         boolean added = mAccounts.add(accountHolder);
         Assert.assertTrue("Account was already added", added);
+        for (AccountsChangeObserver observer : mObservers) {
+            observer.onAccountsChanged();
+        }
     }
 
     /**
@@ -100,6 +116,9 @@
     public void removeAccountHolderExplicitly(AccountHolder accountHolder) {
         boolean removed = mAccounts.remove(accountHolder);
         Assert.assertTrue("Account was already added", removed);
+        for (AccountsChangeObserver observer : mObservers) {
+            observer.onAccountsChanged();
+        }
     }
 
     @Override
diff --git a/components/signin/core/browser/signin_tracker.cc b/components/signin/core/browser/signin_tracker.cc
index b13365d..24e23983 100644
--- a/components/signin/core/browser/signin_tracker.cc
+++ b/components/signin/core/browser/signin_tracker.cc
@@ -32,6 +32,12 @@
   cookie_manager_service_->AddObserver(this);
 }
 
+void SigninTracker::GoogleSigninSucceeded(const std::string& account_id,
+                                          const std::string& username) {
+  if (token_service_->RefreshTokenIsAvailable(account_id))
+    observer_->SigninSuccess();
+}
+
 void SigninTracker::GoogleSigninFailed(const GoogleServiceAuthError& error) {
   observer_->SigninFailed(error);
 }
diff --git a/components/signin/core/browser/signin_tracker.h b/components/signin/core/browser/signin_tracker.h
index cabbedf..e433df587 100644
--- a/components/signin/core/browser/signin_tracker.h
+++ b/components/signin/core/browser/signin_tracker.h
@@ -80,6 +80,8 @@
   ~SigninTracker() override;
 
   // SigninManagerBase::Observer implementation.
+  void GoogleSigninSucceeded(const std::string& account_id,
+                             const std::string& username) override;
   void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
 
   // OAuth2TokenService::Observer implementation.
diff --git a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
index b7c3ff8..717527d3 100644
--- a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
+++ b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
@@ -21,7 +21,6 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/cache_test_util.h"
 #include "content/public/test/content_browser_test.h"
@@ -153,8 +152,6 @@
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ContentBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(
-        switches::kEnableExperimentalWebPlatformFeatures);
 
     // We're redirecting all hosts to localhost even on HTTPS, so we'll get
     // certificate errors.
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 88185f1..f6d1b57 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -87,7 +87,6 @@
 #include "content/public/browser/stream_info.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/common/resource_request.h"
 #include "content/public/common/resource_request_body.h"
 #include "content/public/common/resource_request_completion_status.h"
@@ -347,10 +346,7 @@
       allow_cross_origin_auth_prompt_(false),
       create_download_handler_intercept_(download_handler_intercept),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      io_thread_task_runner_(io_thread_runner),
-      experimental_web_features_enabled_(
-          base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kEnableExperimentalWebPlatformFeatures)) {
+      io_thread_task_runner_(io_thread_runner) {
   DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
   DCHECK(!g_resource_dispatcher_host);
   g_resource_dispatcher_host = this;
@@ -1603,13 +1599,11 @@
         base::MakeUnique<WakeLockResourceThrottle>(request->url().host()));
   }
 
-  // The experimental Clear-Site-Data throttle.
-  if (experimental_web_features_enabled_) {
-    std::unique_ptr<ResourceThrottle> clear_site_data_throttle =
-        ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request);
-    if (clear_site_data_throttle)
-      throttles.push_back(std::move(clear_site_data_throttle));
-  }
+  // The Clear-Site-Data throttle.
+  std::unique_ptr<ResourceThrottle> clear_site_data_throttle =
+      ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request);
+  if (clear_site_data_throttle)
+    throttles.push_back(std::move(clear_site_data_throttle));
 
   // TODO(ricea): Stop looking this up so much.
   ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 0b3d32c..2b95db9 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -830,9 +830,6 @@
   // Task runner for the IO thead.
   scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
 
-  // Whether experimental web platform features are enabled.
-  bool experimental_web_features_enabled_;
-
   DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHostImpl);
 };
 
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 77b4cd77..a92a970 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -298,7 +298,8 @@
       new MockServiceWorkerDatabaseTaskManager(
           base::ThreadTaskRunnerHandle::Get()));
   wrapper_->InitInternal(user_data_directory, std::move(database_task_manager),
-                         base::ThreadTaskRunnerHandle::Get(), nullptr, nullptr);
+                         base::ThreadTaskRunnerHandle::Get(), nullptr, nullptr,
+                         nullptr, nullptr);
   wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id());
   wrapper_->process_manager()->SetNewProcessIdForTest(new_render_process_id());
 
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 751da4a..9933f3c 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -33,11 +33,13 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/service_worker/service_worker_version.h"
+#include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "ipc/ipc_message.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "url/gurl.h"
 
@@ -239,12 +241,16 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
     storage::QuotaManagerProxy* quota_manager_proxy,
     storage::SpecialStoragePolicy* special_storage_policy,
+    base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
+    URLLoaderFactoryGetter* url_loader_factory_getter,
     base::ObserverListThreadSafe<ServiceWorkerContextCoreObserver>*
         observer_list,
     ServiceWorkerContextWrapper* wrapper)
     : wrapper_(wrapper),
       providers_(base::MakeUnique<ProcessToProviderMap>()),
       provider_by_uuid_(base::MakeUnique<ProviderByClientUUIDMap>()),
+      blob_storage_context_(std::move(blob_storage_context)),
+      loader_factory_getter_(url_loader_factory_getter),
       force_update_on_page_load_(false),
       next_handle_id_(0),
       next_registration_handle_id_(0),
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 29120b0..62d6a42 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -35,6 +35,7 @@
 }
 
 namespace storage {
+class BlobStorageContext;
 class QuotaManagerProxy;
 class SpecialStoragePolicy;
 }
@@ -51,6 +52,7 @@
 class ServiceWorkerProviderHost;
 class ServiceWorkerRegistration;
 class ServiceWorkerStorage;
+class URLLoaderFactoryGetter;
 
 // This class manages data associated with service workers.
 // The class is single threaded and should only be used on the IO thread.
@@ -111,6 +113,8 @@
   // ServiceWorkerContextWrapper. When Notify() of |observer_list| is called in
   // ServiceWorkerContextCore, the methods of ServiceWorkerContextCoreObserver
   // will be called on the thread which called AddObserver() of |observer_list|.
+  // |blob_context| and |url_loader_factory_getter| are used only
+  // when IsServicificationEnabled is true.
   ServiceWorkerContextCore(
       const base::FilePath& user_data_directory,
       std::unique_ptr<ServiceWorkerDatabaseTaskManager>
@@ -118,6 +122,8 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
       storage::QuotaManagerProxy* quota_manager_proxy,
       storage::SpecialStoragePolicy* special_storage_policy,
+      base::WeakPtr<storage::BlobStorageContext> blob_context,
+      URLLoaderFactoryGetter* url_loader_factory_getter,
       base::ObserverListThreadSafe<ServiceWorkerContextCoreObserver>*
           observer_list,
       ServiceWorkerContextWrapper* wrapper);
@@ -307,6 +313,14 @@
       int service_worker_provider_id,
       mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info);
 
+  base::WeakPtr<storage::BlobStorageContext> blob_storage_context() {
+    return blob_storage_context_;
+  }
+
+  URLLoaderFactoryGetter* loader_factory_getter() {
+    return loader_factory_getter_.get();
+  }
+
   base::WeakPtr<ServiceWorkerContextCore> AsWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
@@ -380,6 +394,10 @@
   std::map<int, ServiceWorkerNavigationHandleCore*>
       navigation_handle_cores_map_;
 
+  // IsServicificationEnabled
+  base::WeakPtr<storage::BlobStorageContext> blob_storage_context_;
+  scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter_;
+
   bool force_update_on_page_load_;
   int next_handle_id_;
   int next_registration_handle_id_;
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 1c6f549..9b33159 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -31,6 +31,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "net/base/url_util.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/special_storage_policy.h"
 
@@ -183,7 +184,9 @@
 void ServiceWorkerContextWrapper::Init(
     const base::FilePath& user_data_directory,
     storage::QuotaManagerProxy* quota_manager_proxy,
-    storage::SpecialStoragePolicy* special_storage_policy) {
+    storage::SpecialStoragePolicy* special_storage_policy,
+    ChromeBlobStorageContext* blob_context,
+    URLLoaderFactoryGetter* loader_factory_getter) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   is_incognito_ = user_data_directory.empty();
@@ -193,7 +196,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> disk_cache_thread =
       BrowserThread::GetTaskRunnerForThread(BrowserThread::CACHE);
   InitInternal(user_data_directory, std::move(database_task_manager),
-               disk_cache_thread, quota_manager_proxy, special_storage_policy);
+               disk_cache_thread, quota_manager_proxy, special_storage_policy,
+               blob_context, loader_factory_getter);
 }
 
 void ServiceWorkerContextWrapper::Shutdown() {
@@ -842,14 +846,18 @@
     std::unique_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
     const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
     storage::QuotaManagerProxy* quota_manager_proxy,
-    storage::SpecialStoragePolicy* special_storage_policy) {
+    storage::SpecialStoragePolicy* special_storage_policy,
+    ChromeBlobStorageContext* blob_context,
+    URLLoaderFactoryGetter* loader_factory_getter) {
   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&ServiceWorkerContextWrapper::InitInternal, this,
                    user_data_directory, base::Passed(&database_task_manager),
                    disk_cache_thread, base::RetainedRef(quota_manager_proxy),
-                   base::RetainedRef(special_storage_policy)));
+                   base::RetainedRef(special_storage_policy),
+                   base::RetainedRef(blob_context),
+                   base::RetainedRef(loader_factory_getter)));
     return;
   }
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
@@ -860,10 +868,15 @@
   if (quota_manager_proxy) {
     quota_manager_proxy->RegisterClient(new ServiceWorkerQuotaClient(this));
   }
+
+  base::WeakPtr<storage::BlobStorageContext> blob_storage_context =
+      (blob_context && blob_context->context())
+          ? blob_context->context()->AsWeakPtr()
+          : nullptr;
   context_core_.reset(new ServiceWorkerContextCore(
       user_data_directory, std::move(database_task_manager), disk_cache_thread,
-      quota_manager_proxy, special_storage_policy, core_observer_list_.get(),
-      this));
+      quota_manager_proxy, special_storage_policy, blob_storage_context,
+      loader_factory_getter, core_observer_list_.get(), this));
 }
 
 void ServiceWorkerContextWrapper::ShutdownOnIO() {
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index 71f341d..5d871cd4 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -34,9 +34,11 @@
 namespace content {
 
 class BrowserContext;
+class ChromeBlobStorageContext;
 class ResourceContext;
 class ServiceWorkerContextObserver;
 class StoragePartitionImpl;
+class URLLoaderFactoryGetter;
 
 // A refcounted wrapper class for our core object. Higher level content lib
 // classes keep references to this class on mutliple threads. The inner core
@@ -61,9 +63,13 @@
 
   // Init and Shutdown are for use on the UI thread when the profile,
   // storagepartition is being setup and torn down.
+  // |blob_context| and |url_loader_factory_getter| are used only
+  // when IsServicificationEnabled is true.
   void Init(const base::FilePath& user_data_directory,
             storage::QuotaManagerProxy* quota_manager_proxy,
-            storage::SpecialStoragePolicy* special_storage_policy);
+            storage::SpecialStoragePolicy* special_storage_policy,
+            ChromeBlobStorageContext* blob_context,
+            URLLoaderFactoryGetter* url_loader_factory_getter);
   void Shutdown();
 
   // Must be called on the IO thread.
@@ -273,7 +279,9 @@
       std::unique_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager,
       const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
       storage::QuotaManagerProxy* quota_manager_proxy,
-      storage::SpecialStoragePolicy* special_storage_policy);
+      storage::SpecialStoragePolicy* special_storage_policy,
+      ChromeBlobStorageContext* blob_context,
+      URLLoaderFactoryGetter* url_loader_factory_getter);
   void ShutdownOnIO();
 
   void DidFindRegistrationForFindReady(
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index 0cb948b..46ab2ab81 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -981,9 +981,11 @@
   }
 }
 
-void ServiceWorkerDispatcherHost::OnSetHostedVersionId(int provider_id,
-                                                       int64_t version_id,
-                                                       int embedded_worker_id) {
+void ServiceWorkerDispatcherHost::OnSetHostedVersionId(
+    int provider_id,
+    int64_t version_id,
+    int embedded_worker_id,
+    mojom::URLLoaderFactoryAssociatedRequest request) {
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerDispatcherHost::OnSetHostedVersionId");
   if (!GetContext())
@@ -1044,6 +1046,9 @@
 
   provider_host->SetHostedVersion(version);
 
+  if (ServiceWorkerUtils::IsServicificationEnabled())
+    provider_host->CreateScriptURLLoaderFactory(std::move(request));
+
   // Retrieve the registration associated with |version|. The registration
   // must be alive because the version keeps it during starting worker.
   ServiceWorkerRegistration* registration =
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index 3af8eed..a11901c 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -20,6 +20,7 @@
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_associated_interface.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding_set.h"
 
 class GURL;
 
@@ -119,9 +120,11 @@
 
   // mojom::ServiceWorkerDispatcherHost implementation
   void OnProviderCreated(ServiceWorkerProviderHostInfo info) override;
-  void OnSetHostedVersionId(int provider_id,
-                            int64_t version_id,
-                            int embedded_worker_id) override;
+  void OnSetHostedVersionId(
+      int provider_id,
+      int64_t version_id,
+      int embedded_worker_id,
+      mojom::URLLoaderFactoryAssociatedRequest request) override;
 
   // IPC Message handlers
   void OnRegisterServiceWorker(int thread_id,
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index 0ebbc82..2faeab1 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -200,7 +200,7 @@
                               int64_t version_id,
                               int embedded_worker_id) {
     dispatcher_host_->OnSetHostedVersionId(provider_id, version_id,
-                                           embedded_worker_id);
+                                           embedded_worker_id, nullptr);
   }
 
   void SendProviderCreated(ServiceWorkerProviderType type,
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 33b03804..2a40898 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -19,6 +19,7 @@
 #include "content/browser/service_worker/service_worker_handle.h"
 #include "content/browser/service_worker/service_worker_registration_handle.h"
 #include "content/browser/service_worker/service_worker_version.h"
+#include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -31,6 +32,7 @@
 #include "content/public/common/resource_request_body.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "net/base/url_util.h"
+#include "storage/browser/blob/blob_storage_context.h"
 
 namespace content {
 
@@ -96,6 +98,155 @@
   context->RemoveProviderHost(process_id, provider_id);
 }
 
+// Used by a Service Worker for script loading (for all script loading for now,
+// but to be used only during installation once script streaming lands).
+// For now this is just a proxy loader for the network loader.
+// Eventually this should replace the existing URLRequestJob-based request
+// interception for script loading, namely ServiceWorkerWriteToCacheJob.
+// TODO(kinuko): Implement this. Hook up the existing code in
+// ServiceWorkerContextRequestHandler.
+class ScriptURLLoader : public mojom::URLLoader, public mojom::URLLoaderClient {
+ public:
+  ScriptURLLoader(
+      int32_t routing_id,
+      int32_t request_id,
+      uint32_t options,
+      const ResourceRequest& resource_request,
+      mojom::URLLoaderClientPtr client,
+      base::WeakPtr<ServiceWorkerContextCore> context,
+      base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+      base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
+      scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      : network_client_binding_(this),
+        forwarding_client_(std::move(client)),
+        provider_host_(provider_host) {
+    mojom::URLLoaderClientPtr network_client;
+    network_client_binding_.Bind(mojo::MakeRequest(&network_client));
+    loader_factory_getter->GetNetworkFactory()->get()->CreateLoaderAndStart(
+        mojo::MakeRequest(&network_loader_), routing_id, request_id, options,
+        resource_request, std::move(network_client), traffic_annotation);
+  }
+  ~ScriptURLLoader() override {}
+
+  // mojom::URLLoader:
+  void FollowRedirect() override { network_loader_->FollowRedirect(); }
+  void SetPriority(net::RequestPriority priority,
+                   int32_t intra_priority_value) override {
+    network_loader_->SetPriority(priority, intra_priority_value);
+  }
+
+  // mojom::URLLoaderClient for simply proxying network:
+  void OnReceiveResponse(
+      const ResourceResponseHead& response_head,
+      const base::Optional<net::SSLInfo>& ssl_info,
+      mojom::DownloadedTempFilePtr downloaded_file) override {
+    if (provider_host_) {
+      // We don't have complete info here, but fill in what we have now.
+      // At least we need headers and SSL info.
+      net::HttpResponseInfo response_info;
+      response_info.headers = response_head.headers;
+      if (ssl_info.has_value())
+        response_info.ssl_info = *ssl_info;
+      response_info.was_fetched_via_spdy = response_head.was_fetched_via_spdy;
+      response_info.was_alpn_negotiated = response_head.was_alpn_negotiated;
+      response_info.alpn_negotiated_protocol =
+          response_head.alpn_negotiated_protocol;
+      response_info.connection_info = response_head.connection_info;
+      response_info.socket_address = response_head.socket_address;
+
+      DCHECK(provider_host_->IsHostToRunningServiceWorker());
+      provider_host_->running_hosted_version()->SetMainScriptHttpResponseInfo(
+          response_info);
+    }
+    forwarding_client_->OnReceiveResponse(response_head, ssl_info,
+                                          std::move(downloaded_file));
+  }
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         const ResourceResponseHead& response_head) override {
+    forwarding_client_->OnReceiveRedirect(redirect_info, response_head);
+  }
+  void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override {
+    forwarding_client_->OnDataDownloaded(data_len, encoded_data_len);
+  }
+  void OnUploadProgress(int64_t current_position,
+                        int64_t total_size,
+                        OnUploadProgressCallback ack_callback) override {
+    forwarding_client_->OnUploadProgress(current_position, total_size,
+                                         std::move(ack_callback));
+  }
+  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {
+    forwarding_client_->OnReceiveCachedMetadata(data);
+  }
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override {
+    forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
+  }
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override {
+    forwarding_client_->OnStartLoadingResponseBody(std::move(body));
+  }
+  void OnComplete(const ResourceRequestCompletionStatus& status) override {
+    forwarding_client_->OnComplete(status);
+  }
+
+ private:
+  mojom::URLLoaderAssociatedPtr network_loader_;
+  mojo::Binding<mojom::URLLoaderClient> network_client_binding_;
+  mojom::URLLoaderClientPtr forwarding_client_;
+  base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScriptURLLoader);
+};
+
+// Created per one controller worker for script loading (only during
+// installation, eventually). This is kept alive while
+// ServiceWorkerNetworkProvider in the renderer process is alive.
+// Used only when IsServicificationEnabled is true.
+class ScriptURLLoaderFactory : public mojom::URLLoaderFactory {
+ public:
+  ScriptURLLoaderFactory(
+      base::WeakPtr<ServiceWorkerContextCore> context,
+      base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+      base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
+      scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter)
+      : context_(context),
+        provider_host_(provider_host),
+        blob_storage_context_(blob_storage_context),
+        loader_factory_getter_(loader_factory_getter) {}
+  ~ScriptURLLoaderFactory() override {}
+
+  // mojom::URLLoaderFactory:
+  void CreateLoaderAndStart(mojom::URLLoaderAssociatedRequest request,
+                            int32_t routing_id,
+                            int32_t request_id,
+                            uint32_t options,
+                            const ResourceRequest& resource_request,
+                            mojom::URLLoaderClientPtr client,
+                            const net::MutableNetworkTrafficAnnotationTag&
+                                traffic_annotation) override {
+    mojo::MakeStrongAssociatedBinding(
+        base::MakeUnique<ScriptURLLoader>(
+            routing_id, request_id, options, resource_request,
+            std::move(client), context_, provider_host_, blob_storage_context_,
+            loader_factory_getter_, traffic_annotation),
+        std::move(request));
+  }
+
+  void SyncLoad(int32_t routing_id,
+                int32_t request_id,
+                const ResourceRequest& request,
+                SyncLoadCallback callback) override {
+    NOTREACHED();
+  }
+
+ private:
+  base::WeakPtr<ServiceWorkerContextCore> context_;
+  base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+  base::WeakPtr<storage::BlobStorageContext> blob_storage_context_;
+  scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter_;
+  DISALLOW_COPY_AND_ASSIGN(ScriptURLLoaderFactory);
+};
+
 }  // anonymous namespace
 
 ServiceWorkerProviderHost::OneShotGetReadyCallback::OneShotGetReadyCallback(
@@ -332,6 +483,16 @@
   running_hosted_version_ = version;
 }
 
+void ServiceWorkerProviderHost::CreateScriptURLLoaderFactory(
+    mojom::URLLoaderFactoryAssociatedRequest script_loader_factory_request) {
+  DCHECK(ServiceWorkerUtils::IsServicificationEnabled());
+  mojo::MakeStrongAssociatedBinding(
+      base::MakeUnique<ScriptURLLoaderFactory>(
+          context_, AsWeakPtr(), context_->blob_storage_context(),
+          context_->loader_factory_getter()),
+      std::move(script_loader_factory_request));
+}
+
 bool ServiceWorkerProviderHost::IsProviderForClient() const {
   switch (info_.type) {
     case SERVICE_WORKER_PROVIDER_FOR_WINDOW:
@@ -566,6 +727,7 @@
 std::unique_ptr<ServiceWorkerProviderHost>
 ServiceWorkerProviderHost::PrepareForCrossSiteTransfer() {
   DCHECK(!IsBrowserSideNavigationEnabled());
+  DCHECK(!ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK_NE(ChildProcessHost::kInvalidUniqueID, render_process_id_);
   DCHECK_NE(MSG_ROUTING_NONE, info_.route_id);
   DCHECK_EQ(kDocumentMainThreadId, render_thread_id_);
@@ -599,6 +761,7 @@
 void ServiceWorkerProviderHost::CompleteCrossSiteTransfer(
     ServiceWorkerProviderHost* provisional_host) {
   DCHECK(!IsBrowserSideNavigationEnabled());
+  DCHECK(!ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, render_process_id_);
   DCHECK_NE(ChildProcessHost::kInvalidUniqueID, provisional_host->process_id());
   DCHECK_NE(MSG_ROUTING_NONE, provisional_host->frame_id());
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 2229000..a71839a 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -203,6 +203,12 @@
 
   void SetHostedVersion(ServiceWorkerVersion* version);
 
+  // Creates a per-controller-worker URLLoaderFactory for script loading.
+  // The created factory is kept alive while the controller worker is alive.
+  // Used only when IsServicificationEnabled is true.
+  void CreateScriptURLLoaderFactory(
+      mojom::URLLoaderFactoryAssociatedRequest script_loader_factory_request);
+
   // Returns a handler for a request, the handler may return NULL if
   // the request doesn't require special handling.
   std::unique_ptr<ServiceWorkerRequestHandler> CreateRequestHandler(
diff --git a/content/browser/service_worker/service_worker_url_loader_job.cc b/content/browser/service_worker/service_worker_url_loader_job.cc
index 89bbdf42..7e10fab 100644
--- a/content/browser/service_worker/service_worker_url_loader_job.cc
+++ b/content/browser/service_worker/service_worker_url_loader_job.cc
@@ -182,9 +182,7 @@
 void ServiceWorkerURLLoaderJob::CommitResponseHeaders() {
   DCHECK_EQ(Status::kStarted, status_);
   status_ = Status::kSentHeader;
-  url_loader_client_->OnReceiveResponse(
-      response_head_, base::nullopt /* TODO(scottmg): ssl info */,
-      mojom::DownloadedTempFilePtr());
+  url_loader_client_->OnReceiveResponse(response_head_, ssl_info_, nullptr);
 }
 
 void ServiceWorkerURLLoaderJob::CommitCompleted(int error_code) {
@@ -247,6 +245,15 @@
     return;
   }
 
+  // Creates a new HttpResponseInfo using the the ServiceWorker script's
+  // HttpResponseInfo to show HTTPS padlock.
+  // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
+  // ServiceWorker, we have to check the security level of the responses.
+  const net::HttpResponseInfo* main_script_http_info =
+      version->GetMainScriptHttpResponseInfo();
+  DCHECK(main_script_http_info);
+  ssl_info_ = main_script_http_info->ssl_info;
+
   std::move(loader_callback_)
       .Run(base::Bind(&ServiceWorkerURLLoaderJob::StartResponse,
                       weak_factory_.GetWeakPtr(), response,
@@ -268,9 +275,6 @@
   SaveResponseHeaders(response.status_code, response.status_text,
                       response.headers);
 
-  // TODO(kinuko): Set the script's HTTP response info via
-  // version->GetMainScriptHttpResponseInfo() for HTTPS padlock.
-
   // Ideally, we would always get a data pipe fom SWFetchDispatcher and use
   // this case. See:
   // https://docs.google.com/a/google.com/document/d/1_ROmusFvd8ATwIZa29-P6Ls5yyLjfld0KvKchVfA84Y/edit?usp=drive_web
diff --git a/content/browser/service_worker/service_worker_url_loader_job.h b/content/browser/service_worker/service_worker_url_loader_job.h
index 86037a2..ce479ea7 100644
--- a/content/browser/service_worker/service_worker_url_loader_job.h
+++ b/content/browser/service_worker/service_worker_url_loader_job.h
@@ -156,6 +156,7 @@
 
   bool did_navigation_preload_ = false;
   ResourceResponseHead response_head_;
+  base::Optional<net::SSLInfo> ssl_info_;
 
   // URLLoaderClient binding for loading a blob.
   mojo::Binding<mojom::URLLoaderClient> blob_client_binding_;
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 578247e4..6faa3fd 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -903,13 +903,7 @@
 }
 
 void ServiceWorkerVersion::OnScriptLoaded() {
-  DCHECK(GetMainScriptHttpResponseInfo() ||
-         // TODO(scottmg|falken): This DCHECK is currently triggered in
-         // --network-service because ServiceWorkerReadFromCacheJob isn't being
-         // used to retrieve the service worker js. This should be removed once
-         // that's done.
-         (IsBrowserSideNavigationEnabled() &&
-          base::FeatureList::IsEnabled(features::kNetworkService)));
+  DCHECK(GetMainScriptHttpResponseInfo());
   if (IsInstalled(status()))
     UMA_HISTOGRAM_BOOLEAN("ServiceWorker.ScriptLoadSuccess", true);
 }
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 9bc18af4c..4ff9f579 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -497,8 +497,6 @@
   partition->cache_storage_context_->Init(path, quota_manager_proxy);
 
   partition->service_worker_context_ = new ServiceWorkerContextWrapper(context);
-  partition->service_worker_context_->Init(path, quota_manager_proxy.get(),
-                                           context->GetSpecialStoragePolicy());
   partition->service_worker_context_->set_storage_partition(partition.get());
 
   partition->appcache_service_ =
@@ -530,6 +528,9 @@
 
   partition->bluetooth_allowed_devices_map_ = new BluetoothAllowedDevicesMap();
 
+  scoped_refptr<ChromeBlobStorageContext> blob_context =
+      ChromeBlobStorageContext::GetFor(context);
+
   if (base::FeatureList::IsEnabled(features::kNetworkService)) {
     static mojom::NetworkServicePtr* g_network_service =
         new mojom::NetworkServicePtr;
@@ -546,8 +547,6 @@
         ->CreateNetworkContext(MakeRequest(&partition->network_context_),
                                std::move(context_params));
 
-    scoped_refptr<ChromeBlobStorageContext> blob_context =
-        ChromeBlobStorageContext::GetFor(context);
     BlobURLLoaderFactory::BlobContextGetter blob_getter =
         base::BindOnce(&BlobStorageContextGetter, blob_context);
     partition->blob_url_loader_factory_ = BlobURLLoaderFactory::Create(
@@ -557,6 +556,10 @@
     partition->url_loader_factory_getter_->Initialize(partition.get());
   }
 
+  partition->service_worker_context_->Init(
+      path, quota_manager_proxy.get(), context->GetSpecialStoragePolicy(),
+      blob_context.get(), partition->url_loader_factory_getter_.get());
+
   return partition;
 }
 
diff --git a/content/child/service_worker/service_worker_network_provider.cc b/content/child/service_worker/service_worker_network_provider.cc
index 364d0dc..e609bd0a 100644
--- a/content/child/service_worker/service_worker_network_provider.cc
+++ b/content/child/service_worker/service_worker_network_provider.cc
@@ -193,6 +193,7 @@
   context_ = new ServiceWorkerProviderContext(
       provider_id_, provider_type, std::move(client_request),
       ChildThreadImpl::current()->thread_safe_sender());
+
   ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
       &dispatcher_host_);
   dispatcher_host_->OnProviderCreated(std::move(host_info));
@@ -224,8 +225,9 @@
   DCHECK_NE(kInvalidServiceWorkerProviderId, provider_id_);
   if (!ChildThreadImpl::current())
     return;  // May be null in some tests.
-  dispatcher_host_->OnSetHostedVersionId(provider_id(), version_id,
-                                         embedded_worker_id);
+  dispatcher_host_->OnSetHostedVersionId(
+      provider_id(), version_id, embedded_worker_id,
+      mojo::MakeRequest(&script_loader_factory_));
 }
 
 bool ServiceWorkerNetworkProvider::IsControlledByServiceWorker() const {
diff --git a/content/child/service_worker/service_worker_network_provider.h b/content/child/service_worker/service_worker_network_provider.h
index 113d260..94daf22 100644
--- a/content/child/service_worker/service_worker_network_provider.h
+++ b/content/child/service_worker/service_worker_network_provider.h
@@ -72,6 +72,12 @@
   // ServiceWorkerVersion should be used.
   void SetServiceWorkerVersionId(int64_t version_id, int embedded_worker_id);
 
+  mojom::URLLoaderFactory* script_loader_factory() {
+    if (script_loader_factory_.is_bound())
+      return script_loader_factory_.get();
+    return nullptr;
+  }
+
   bool IsControlledByServiceWorker() const;
 
  private:
@@ -79,6 +85,8 @@
   scoped_refptr<ServiceWorkerProviderContext> context_;
   mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_;
   mojom::ServiceWorkerProviderHostAssociatedPtr provider_host_;
+  mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerNetworkProvider);
 };
 
diff --git a/content/common/service_worker/service_worker.mojom b/content/common/service_worker/service_worker.mojom
index 7e8f5c8..344edf9 100644
--- a/content/common/service_worker/service_worker.mojom
+++ b/content/common/service_worker/service_worker.mojom
@@ -5,6 +5,7 @@
 module content.mojom;
 
 import "content/common/service_worker/service_worker_provider.mojom";
+import "content/public/common/url_loader_factory.mojom";
 
 // Per-process browser-side interface.
 // The renderer uses this interface to tell the browser when potential service
@@ -20,8 +21,12 @@
   // Informs the browser that a service worker is starting up. |provider_id|
   // identifies the ServiceWorkerProviderHost hosting the service
   // worker. |version_id| identifies the ServiceWorkerVersion and
-  // |embedded_worker_id| identifies the EmbeddedWorkerInstance.  
+  // |embedded_worker_id| identifies the EmbeddedWorkerInstance.
+  // If ServiceWorkerUtils::IsServicificationEnabled() is true, this also
+  // populates |script_loader_factory| for script loading for the service
+  // worker.
   OnSetHostedVersionId(int32 provider_id,
                        int64 version_id,
-                       int32 embedded_worker_id);
-};
\ No newline at end of file
+                       int32 embedded_worker_id,
+                       associated URLLoaderFactory& script_loader_factory);
+};
diff --git a/content/common/service_worker/service_worker_provider.mojom b/content/common/service_worker/service_worker_provider.mojom
index 4016e42..922135f 100644
--- a/content/common/service_worker/service_worker_provider.mojom
+++ b/content/common/service_worker/service_worker_provider.mojom
@@ -18,4 +18,4 @@
   bool is_parent_frame_secure;
   associated ServiceWorkerProviderHost& host_request;
   associated ServiceWorkerProvider client_ptr_info;
-};
\ No newline at end of file
+};
diff --git a/content/public/utility/utility_thread.h b/content/public/utility/utility_thread.h
index 49aa8a6..6d1c3627 100644
--- a/content/public/utility/utility_thread.h
+++ b/content/public/utility/utility_thread.h
@@ -18,8 +18,8 @@
   UtilityThread();
   ~UtilityThread() override;
 
-  // Releases the process. TODO(noel): rename this routine and update callers.
-  virtual void ReleaseProcessIfNeeded() = 0;
+  // Releases the process.
+  virtual void ReleaseProcess() = 0;
 
   // Initializes blink if it hasn't already been initialized.
   virtual void EnsureBlinkInitialized() = 0;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index ca2c49f..580e320 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -770,6 +770,7 @@
       "//third_party/webrtc/api:video_frame_api",
       "//third_party/webrtc/api/audio_codecs:builtin_audio_decoder_factory",
       "//third_party/webrtc/api/audio_codecs:builtin_audio_encoder_factory",
+      "//third_party/webrtc/common_video:common_video",
       "//third_party/webrtc/media:rtc_media",
       "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/audio_device",
diff --git a/content/renderer/media/gpu/rtc_video_encoder.cc b/content/renderer/media/gpu/rtc_video_encoder.cc
index 2185d7e..8c671e5 100644
--- a/content/renderer/media/gpu/rtc_video_encoder.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder.cc
@@ -47,25 +47,16 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(RTCTimestamps);
 };
 
-// Translate from webrtc::VideoCodecType and webrtc::VideoCodec to
-// media::VideoCodecProfile.
-media::VideoCodecProfile WebRTCVideoCodecToVideoCodecProfile(
-    webrtc::VideoCodecType type,
-    const webrtc::VideoCodec* codec_settings) {
-  DCHECK_EQ(type, codec_settings->codecType);
-  switch (type) {
-    case webrtc::kVideoCodecVP8:
-      return media::VP8PROFILE_ANY;
-    case webrtc::kVideoCodecVP9:
-      return media::VP9PROFILE_MIN;
-    case webrtc::kVideoCodecH264:
-      // TODO(magjed): WebRTC is only using Baseline profile for now. Update
-      // once http://crbug/webrtc/6337 is fixed.
-      return media::H264PROFILE_BASELINE;
-    default:
-      NOTREACHED() << "Unrecognized video codec type";
-      return media::VIDEO_CODEC_PROFILE_UNKNOWN;
+webrtc::VideoCodecType ProfileToWebRtcVideoCodecType(
+    media::VideoCodecProfile profile) {
+  if (profile >= media::VP8PROFILE_MIN && profile <= media::VP8PROFILE_MAX) {
+    return webrtc::kVideoCodecVP8;
+  } else if (profile >= media::H264PROFILE_MIN &&
+             profile <= media::H264PROFILE_MAX) {
+    return webrtc::kVideoCodecH264;
   }
+  NOTREACHED() << "Invalid profile " << GetProfileName(profile);
+  return webrtc::kVideoCodecUnknown;
 }
 
 // Populates struct webrtc::RTPFragmentationHeader for H264 codec.
@@ -792,12 +783,12 @@
 }
 
 RTCVideoEncoder::RTCVideoEncoder(
-    webrtc::VideoCodecType type,
+    media::VideoCodecProfile profile,
     media::GpuVideoAcceleratorFactories* gpu_factories)
-    : video_codec_type_(type),
+    : profile_(profile),
       gpu_factories_(gpu_factories),
       gpu_task_runner_(gpu_factories->GetTaskRunner()) {
-  DVLOG(1) << "RTCVideoEncoder(): codec type=" << type;
+  DVLOG(1) << "RTCVideoEncoder(): profile=" << GetProfileName(profile);
 }
 
 RTCVideoEncoder::~RTCVideoEncoder() {
@@ -818,9 +809,7 @@
     Release();
   }
 
-  impl_ = new Impl(gpu_factories_, video_codec_type_);
-  const media::VideoCodecProfile profile = WebRTCVideoCodecToVideoCodecProfile(
-      impl_->video_codec_type(), codec_settings);
+  impl_ = new Impl(gpu_factories_, ProfileToWebRtcVideoCodecType(profile_));
 
   base::WaitableEvent initialization_waiter(
       base::WaitableEvent::ResetPolicy::MANUAL,
@@ -828,17 +817,14 @@
   int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   gpu_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA,
-                 impl_,
+      base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA, impl_,
                  gfx::Size(codec_settings->width, codec_settings->height),
-                 codec_settings->startBitrate,
-                 profile,
-                 &initialization_waiter,
+                 codec_settings->startBitrate, profile_, &initialization_waiter,
                  &initialization_retval));
 
   // webrtc::VideoEncoder expects this call to be synchronous.
   initialization_waiter.Wait();
-  RecordInitEncodeUMA(initialization_retval, profile);
+  RecordInitEncodeUMA(initialization_retval, profile_);
   return initialization_retval;
 }
 
diff --git a/content/renderer/media/gpu/rtc_video_encoder.h b/content/renderer/media/gpu/rtc_video_encoder.h
index bef4f21c..67d59b3 100644
--- a/content/renderer/media/gpu/rtc_video_encoder.h
+++ b/content/renderer/media/gpu/rtc_video_encoder.h
@@ -41,7 +41,7 @@
 class CONTENT_EXPORT RTCVideoEncoder
     : NON_EXPORTED_BASE(public webrtc::VideoEncoder) {
  public:
-  RTCVideoEncoder(webrtc::VideoCodecType type,
+  RTCVideoEncoder(media::VideoCodecProfile profile,
                   media::GpuVideoAcceleratorFactories* gpu_factories);
   ~RTCVideoEncoder() override;
 
@@ -68,8 +68,7 @@
   void RecordInitEncodeUMA(int32_t init_retval,
                            media::VideoCodecProfile profile);
 
-  // The video codec type, as reported to WebRTC.
-  const webrtc::VideoCodecType video_codec_type_;
+  const media::VideoCodecProfile profile_;
 
   // Factory for creating VEAs, shared memory buffers, etc.
   media::GpuVideoAcceleratorFactories* gpu_factories_;
diff --git a/content/renderer/media/gpu/rtc_video_encoder_factory.cc b/content/renderer/media/gpu/rtc_video_encoder_factory.cc
index f3189de..bbece0d 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_factory.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder_factory.cc
@@ -11,23 +11,23 @@
 #include "content/renderer/media/gpu/rtc_video_encoder.h"
 #include "media/gpu/ipc/client/gpu_video_encode_accelerator_host.h"
 #include "media/renderers/gpu_video_accelerator_factories.h"
-#include "media/video/video_encode_accelerator.h"
+#include "third_party/webrtc/common_video/h264/profile_level_id.h"
 
 namespace content {
 
 namespace {
 
 // Translate from media::VideoEncodeAccelerator::SupportedProfile to
-// one or more instances of cricket::WebRtcVideoEncoderFactory::VideoCodec
-void VEAToWebRTCCodecs(
-    std::vector<cricket::VideoCodec>* codecs,
+// cricket::WebRtcVideoEncoderFactory::VideoCodec, or return nothing if the
+// profile isn't supported.
+base::Optional<cricket::VideoCodec> VEAToWebRTCCodec(
     const media::VideoEncodeAccelerator::SupportedProfile& profile) {
   DCHECK_EQ(profile.max_framerate_denominator, 1U);
 
   if (profile.profile >= media::VP8PROFILE_MIN &&
       profile.profile <= media::VP8PROFILE_MAX) {
     if (base::FeatureList::IsEnabled(features::kWebRtcHWVP8Encoding)) {
-      codecs->push_back(cricket::VideoCodec("VP8"));
+      return base::Optional<cricket::VideoCodec>(cricket::VideoCodec("VP8"));
     }
   } else if (profile.profile >= media::H264PROFILE_MIN &&
              profile.profile <= media::H264PROFILE_MAX) {
@@ -41,10 +41,41 @@
 #endif  // BUILDFLAG(RTC_USE_H264) && !defined(MEDIA_DISABLE_FFMPEG)
     if (webrtc_h264_sw_enabled ||
         base::FeatureList::IsEnabled(features::kWebRtcHWH264Encoding)) {
-      // TODO(magjed): Propagate H264 profile information.
-      codecs->push_back(cricket::VideoCodec("H264"));
+      webrtc::H264::Profile h264_profile;
+      switch (profile.profile) {
+        case media::H264PROFILE_BASELINE:
+          h264_profile = webrtc::H264::kProfileBaseline;
+          break;
+        case media::H264PROFILE_MAIN:
+          h264_profile = webrtc::H264::kProfileMain;
+          break;
+        case media::H264PROFILE_HIGH:
+          h264_profile = webrtc::H264::kProfileHigh;
+          break;
+        default:
+          // Unsupported H264 profile in WebRTC.
+          return base::Optional<cricket::VideoCodec>();
+      }
+
+      const int width = profile.max_resolution.width();
+      const int height = profile.max_resolution.height();
+      const int fps = profile.max_framerate_numerator;
+      DCHECK_EQ(1u, profile.max_framerate_denominator);
+
+      const rtc::Optional<webrtc::H264::Level> h264_level =
+          webrtc::H264::SupportedLevel(width * height, fps);
+      const webrtc::H264::ProfileLevelId profile_level_id(
+          h264_profile, h264_level.value_or(webrtc::H264::kLevel1));
+
+      cricket::VideoCodec codec("H264");
+      codec.SetParam(cricket::kH264FmtpProfileLevelId,
+                     *webrtc::H264::ProfileLevelIdToString(profile_level_id));
+      codec.SetParam(cricket::kH264FmtpLevelAsymmetryAllowed, "1");
+      codec.SetParam(cricket::kH264FmtpPacketizationMode, "1");
+      return base::Optional<cricket::VideoCodec>(codec);
     }
   }
+  return base::Optional<cricket::VideoCodec>();
 }
 
 }  // anonymous namespace
@@ -54,20 +85,36 @@
     : gpu_factories_(gpu_factories) {
   const media::VideoEncodeAccelerator::SupportedProfiles& profiles =
       gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles();
-  for (const auto& profile : profiles)
-    VEAToWebRTCCodecs(&supported_codecs_, profile);
+  for (const auto& profile : profiles) {
+    base::Optional<cricket::VideoCodec> codec = VEAToWebRTCCodec(profile);
+    if (codec) {
+      supported_codecs_.push_back(std::move(*codec));
+      profiles_.push_back(profile.profile);
+    }
+  }
+  // There should be a 1:1 mapping between media::VideoCodecProfile and
+  // cricket::VideoCodec.
+  CHECK_EQ(profiles_.size(), supported_codecs_.size());
 }
 
 RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {}
 
 webrtc::VideoEncoder* RTCVideoEncoderFactory::CreateVideoEncoder(
     const cricket::VideoCodec& codec) {
-  for (const cricket::VideoCodec& supported_codec : supported_codecs_) {
-    if (cricket::CodecNamesEq(codec.name, supported_codec.name)) {
-      webrtc::VideoCodecType type = webrtc::PayloadNameToCodecType(codec.name)
-                                        .value_or(webrtc::kVideoCodecUnknown);
-      return new RTCVideoEncoder(type, gpu_factories_);
+  for (size_t i = 0; i < supported_codecs_.size(); ++i) {
+    if (!cricket::CodecNamesEq(codec.name, supported_codecs_[i].name))
+      continue;
+    // Check H264 profile.
+    using webrtc::H264::ParseSdpProfileLevelId;
+    if (cricket::CodecNamesEq(codec.name.c_str(), cricket::kH264CodecName) &&
+        ParseSdpProfileLevelId(codec.params)->profile !=
+            ParseSdpProfileLevelId(supported_codecs_[i].params)->profile) {
+      continue;
     }
+    // There should be a 1:1 mapping between media::VideoCodecProfile and
+    // cricket::VideoCodec.
+    CHECK_EQ(profiles_.size(), supported_codecs_.size());
+    return new RTCVideoEncoder(profiles_[i], gpu_factories_);
   }
   return nullptr;
 }
diff --git a/content/renderer/media/gpu/rtc_video_encoder_factory.h b/content/renderer/media/gpu/rtc_video_encoder_factory.h
index 307f8f92..c5bcde0 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_factory.h
+++ b/content/renderer/media/gpu/rtc_video_encoder_factory.h
@@ -39,6 +39,9 @@
   media::GpuVideoAcceleratorFactories* gpu_factories_;
 
   // List of supported cricket::WebRtcVideoEncoderFactory::VideoCodec.
+  // |profiles_| and |supported_codecs_| have the same length and the profile
+  // for |supported_codecs_[i]| is |profiles_[i]|.
+  std::vector<media::VideoCodecProfile> profiles_;
   std::vector<cricket::VideoCodec> supported_codecs_;
 
   DISALLOW_COPY_AND_ASSIGN(RTCVideoEncoderFactory);
diff --git a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
index 3fd7a91b..889b024 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
@@ -113,7 +113,19 @@
 
   void CreateEncoder(webrtc::VideoCodecType codec_type) {
     DVLOG(3) << __func__;
-    rtc_encoder_ = base::MakeUnique<RTCVideoEncoder>(codec_type,
+    media::VideoCodecProfile media_profile;
+    switch (codec_type) {
+      case webrtc::kVideoCodecVP8:
+        media_profile = media::VP8PROFILE_ANY;
+        break;
+      case webrtc::kVideoCodecH264:
+        media_profile = media::H264PROFILE_BASELINE;
+        break;
+      default:
+        ADD_FAILURE() << "Unexpected codec type: " << codec_type;
+        media_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
+    }
+    rtc_encoder_ = base::MakeUnique<RTCVideoEncoder>(media_profile,
                                                      mock_gpu_factories_.get());
   }
 
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc
index ef941bec..568052e5 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -65,12 +65,9 @@
           hardware_params.sample_rate(), 0);
       break;
     case media::AudioLatency::LATENCY_EXACT_MS:
-      // TODO(andrew.macpherson@soundtrap.com): http://crbug.com/708917
-      return std::min(4096,
-                      media::AudioLatency::GetExactBufferSize(
-                          base::TimeDelta::FromSecondsD(latency_hint.Seconds()),
-                          hardware_params.sample_rate(),
-                          hardware_params.frames_per_buffer()));
+      return media::AudioLatency::GetExactBufferSize(
+          base::TimeDelta::FromSecondsD(latency_hint.Seconds()),
+          hardware_params.sample_rate(), hardware_params.frames_per_buffer());
       break;
     default:
       NOTREACHED();
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index f88d4d3..92be82d 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -38,6 +38,7 @@
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_utils.h"
 #include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/push_event_payload.h"
@@ -103,6 +104,19 @@
     request.SetExtraData(extra_data.release());
   }
 
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
+      const blink::WebURLRequest& request,
+      base::SingleThreadTaskRunner* task_runner) override {
+    RenderThreadImpl* child_thread = RenderThreadImpl::current();
+    if (child_thread && provider_->script_loader_factory() &&
+        ServiceWorkerUtils::IsServicificationEnabled()) {
+      return base::MakeUnique<WebURLLoaderImpl>(
+          child_thread->resource_dispatcher(), task_runner,
+          provider_->script_loader_factory());
+    }
+    return nullptr;
+  }
+
   int GetProviderID() const override { return provider_->provider_id(); }
 
  private:
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.cc b/content/shell/browser/layout_test/layout_test_browser_context.cc
index 3ba2ba7..d8ccd83 100644
--- a/content/shell/browser/layout_test/layout_test_browser_context.cc
+++ b/content/shell/browser/layout_test/layout_test_browser_context.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/push_messaging_service.h"
@@ -73,7 +74,9 @@
 
     return new net::URLRequestFileJob(
         request, network_delegate, path,
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE));
+        base::CreateTaskRunnerWithTraits(
+            {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+             base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
   }
 };
 
diff --git a/content/shell/browser/shell_download_manager_delegate.cc b/content/shell/browser/shell_download_manager_delegate.cc
index 789b40c..5156d50 100644
--- a/content/shell/browser/shell_download_manager_delegate.cc
+++ b/content/shell/browser/shell_download_manager_delegate.cc
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -83,16 +84,14 @@
                  download->GetId(),
                  callback);
 
-  BrowserThread::PostTask(
-      BrowserThread::FILE,
+  PostTaskWithTraits(
       FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+       base::TaskPriority::USER_VISIBLE},
       base::Bind(&ShellDownloadManagerDelegate::GenerateFilename,
-                 download->GetURL(),
-                 download->GetContentDisposition(),
-                 download->GetSuggestedFilename(),
-                 download->GetMimeType(),
-                 default_download_path_,
-                 filename_determined_callback));
+                 download->GetURL(), download->GetContentDisposition(),
+                 download->GetSuggestedFilename(), download->GetMimeType(),
+                 default_download_path_, filename_determined_callback));
   return true;
 }
 
@@ -116,7 +115,6 @@
     const std::string& mime_type,
     const base::FilePath& suggested_directory,
     const FilenameDeterminedCallback& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   base::FilePath generated_name = net::GenerateFileName(url,
                                                         content_disposition,
                                                         std::string(),
diff --git a/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc b/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
index bf0ffca..b275d6a 100644
--- a/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
+++ b/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
@@ -217,11 +217,9 @@
           media::AudioLatency::GetHighLatencyBufferSize(hw_sample_rate, 0);
       break;
     case blink::WebAudioLatencyHint::kCategoryExact:
-      // TODO(andrew.macpherson@soundtrap.com): http://crbug.com/708917
-      buffer_size = std::min(
-          4096, media::AudioLatency::GetExactBufferSize(
-                    base::TimeDelta::FromSecondsD(latency_hint.Seconds()),
-                    hw_sample_rate, hw_buffer_size));
+      buffer_size = media::AudioLatency::GetExactBufferSize(
+          base::TimeDelta::FromSecondsD(latency_hint.Seconds()), hw_sample_rate,
+          hw_buffer_size);
       break;
     default:
       NOTREACHED();
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc
index a4eedeed..abe5f28 100644
--- a/content/utility/utility_service_factory.cc
+++ b/content/utility/utility_service_factory.cc
@@ -122,14 +122,14 @@
 }
 
 void UtilityServiceFactory::OnServiceQuit() {
-  UtilityThread::Get()->ReleaseProcessIfNeeded();
+  UtilityThread::Get()->ReleaseProcess();
 }
 
 void UtilityServiceFactory::OnLoadFailed() {
   UtilityThreadImpl* utility_thread =
       static_cast<UtilityThreadImpl*>(UtilityThread::Get());
   utility_thread->Shutdown();
-  utility_thread->ReleaseProcessIfNeeded();
+  utility_thread->ReleaseProcess();
 }
 
 std::unique_ptr<service_manager::Service>
diff --git a/content/utility/utility_thread_impl.cc b/content/utility/utility_thread_impl.cc
index d7059fd..fd49df6 100644
--- a/content/utility/utility_thread_impl.cc
+++ b/content/utility/utility_thread_impl.cc
@@ -39,7 +39,7 @@
   ChildThreadImpl::Shutdown();
 }
 
-void UtilityThreadImpl::ReleaseProcessIfNeeded() {
+void UtilityThreadImpl::ReleaseProcess() {
   if (!IsInBrowserProcess()) {
     ChildProcess::current()->ReleaseProcess();
     return;
diff --git a/content/utility/utility_thread_impl.h b/content/utility/utility_thread_impl.h
index 9bfdc75..ac132ff 100644
--- a/content/utility/utility_thread_impl.h
+++ b/content/utility/utility_thread_impl.h
@@ -39,7 +39,7 @@
   void Shutdown() override;
 
   // UtilityThread:
-  void ReleaseProcessIfNeeded() override;
+  void ReleaseProcess() override;
   void EnsureBlinkInitialized() override;
 
  private:
diff --git a/gin/v8_platform_unittest.cc b/gin/v8_platform_unittest.cc
index e3643d3da..3a5abe3 100644
--- a/gin/v8_platform_unittest.cc
+++ b/gin/v8_platform_unittest.cc
@@ -8,7 +8,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 class TestTraceStateObserver
-    : public NON_EXPORTED_BASE(v8::Platform::TraceStateObserver) {
+    : public NON_EXPORTED_BASE(v8::TracingController::TraceStateObserver) {
  public:
   void OnTraceEnabled() final { ++enabled_; }
   void OnTraceDisabled() final { ++disabled_; }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 384124f2..d692ed5 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -167,9 +167,12 @@
 
 generated_devtools_api = []
 generated_devtools_api_js = []
+generated_devtools_api_js_externs = []
 foreach(domain, devtools_domains) {
   generated_devtools_api_js +=
       [ "$target_gen_dir/public/devtools_js/" + domain + ".js" ]
+  generated_devtools_api_js_externs +=
+      [ "$target_gen_dir/public/devtools_js/externs/externs_" + domain + ".js" ]
   generated_devtools_api += [
     "$target_gen_dir/public/devtools/domains/" + domain + ".cc",
     "$target_gen_dir/public/devtools/domains/" + domain + ".h",
@@ -191,10 +194,12 @@
     "$root_gen_dir/blink/core/inspector/protocol.json",
   ]
 
-  outputs = generated_devtools_api + generated_devtools_api_js
+  outputs = generated_devtools_api + generated_devtools_api_js +
+            generated_devtools_api_js_externs
 
   sources = [
     "lib/browser/devtools_api/domain_cc.template",
+    "lib/browser/devtools_api/domain_externs_js.template",
     "lib/browser/devtools_api/domain_h.template",
     "lib/browser/devtools_api/domain_js.template",
     "lib/browser/devtools_api/domain_type_conversions_h.template",
diff --git a/headless/lib/browser/devtools_api/client_api_generator.py b/headless/lib/browser/devtools_api/client_api_generator.py
index 7a8ea343..d2ce3de 100644
--- a/headless/lib/browser/devtools_api/client_api_generator.py
+++ b/headless/lib/browser/devtools_api/client_api_generator.py
@@ -518,6 +518,10 @@
       jinja_env, os.path.join(output_dirname, 'devtools_js'), json_api,
       'domain', ['js'],
       lambda domain_name: domain_name)
+  GeneratePerDomain(
+      jinja_env, os.path.join(output_dirname, 'devtools_js', 'externs'),
+      json_api, 'domain_externs', ['js'],
+      lambda domain_name: 'externs_%s' % (domain_name, ))
 
 
 def GenerateTypes(jinja_env, output_dirname, json_api):
diff --git a/headless/lib/browser/devtools_api/devtools_connection.js b/headless/lib/browser/devtools_api/devtools_connection.js
index d31d4add3..3061c196 100644
--- a/headless/lib/browser/devtools_api/devtools_connection.js
+++ b/headless/lib/browser/devtools_api/devtools_connection.js
@@ -10,13 +10,14 @@
 
 'use strict';
 
-goog.module('chromium.DevTools.Connection');
+goog.provide('chromium.DevTools.Connection');
 
 /**
  * Handles sending and receiving DevTools JSON protocol messages over the
  * provided low level message transport.
+ * @export
  */
-class Connection {
+chromium.DevTools.Connection = class {
   /**
    * @param {!Object} transport The API providing transport for devtools
    *     commands.
@@ -31,7 +32,7 @@
     /**
      * An object containing pending DevTools protocol commands keyed by id.
      *
-     * @private {!Map<number, !Connection.PendingCommand>}
+     * @private {!Map<number, !chromium.DevTools.Connection.PendingCommand>}
      */
     this.pendingCommands_ = new Map();
 
@@ -42,7 +43,9 @@
      * An object containing DevTools protocol events we are listening for keyed
      * by name.
      *
-     * @private {!Map<string, !Map<number, !Connection.EventFunction>>}
+     * @private {!Map<string,
+     *                !Map<number,
+     *                     !chromium.DevTools.Connection.EventFunction>>}
      */
     this.eventListeners_ = new Map();
 
@@ -62,8 +65,9 @@
    *
    * @param {string} eventName Name of the DevTools protocol event to listen
    *     for.
-   * @param {!Connection.EventFunction} listener The callback issued when we
-   *     receive a DevTools protocol event corresponding to the given name.
+   * @param {!chromium.DevTools.Connection.EventFunction} listener The callback
+   *     issued when we receive a DevTools protocol event corresponding to the
+   *     given name.
    * @return {number} The id of this event listener.
    */
   addEventListener(eventName, listener) {
@@ -100,7 +104,8 @@
    * @param {string} method The name of the DevTools protocol command method.
    * @param {!Object=} params An object containing the command parameters if
    *     any.
-   * @return {!Promise<!Object>} A promise for the results object.
+   * @return {!Promise<!TYPE>} A promise for the results object.
+   * @template TYPE
    */
   sendDevToolsMessage(method, params = {}) {
     let id = this.commandId_;
@@ -165,7 +170,7 @@
 /**
  * @typedef {function(Object): undefined|function(string): undefined}
  */
-Connection.EventFunction;
+chromium.DevTools.Connection.EventFunction;
 
 /**
  * @typedef {{
@@ -173,6 +178,4 @@
  *    reject: function(!Object)
  * }}
  */
-Connection.PendingCommand;
-
-exports = Connection;
+chromium.DevTools.Connection.PendingCommand;
diff --git a/headless/lib/browser/devtools_api/domain_externs_js.template b/headless/lib/browser/devtools_api/domain_externs_js.template
new file mode 100644
index 0000000..0f9c6e0
--- /dev/null
+++ b/headless/lib/browser/devtools_api/domain_externs_js.template
@@ -0,0 +1,176 @@
+// 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.
+
+{# Changes to the externs must be reflected in the generated API too. #}
+
+/**
+ * Experimental bindings for the {{domain.domain}} DevTools Domain.  Note these
+ * are subject to change without warning. Use at your own risk.
+ * @param {!chromium.DevTools.Connection} connection The DevTools connection.
+ * @constructor
+ */
+chromium.DevTools.Experimental{{domain.domain}} = function(connection) {};
+
+/**
+ * Removes an event listener.
+ *
+ * @param {number} id The id of the event listener to remove.
+ * @return {boolean} Whether the event listener was actually removed.
+ */
+chromium.DevTools.Experimental{{domain.domain}}.prototype.removeEventListener = function(id) {};
+
+/**
+ * Bindings for the {{domain.domain}} DevTools Domain.
+ * @param {!chromium.DevTools.Connection} connection The DevTools connection.
+ * @constructor
+ */
+chromium.DevTools.{{domain.domain}} = function(connection) {};
+
+/** @type {!chromium.DevTools.Experimental{{domain.domain}}} */
+chromium.DevTools.{{domain.domain}}.prototype.experimental;
+
+/**
+ * Removes an event listener.
+ *
+ * @param {number} id The id of the event listener to remove.
+ * @return {boolean} Whether the event listener was actually removed.
+ */
+chromium.DevTools.{{domain.domain}}.prototype.removeEventListener = function(id) {};
+
+{# Generate enums. #}
+{% for type in domain.types %}
+  {% if not "enum" in type %}{% continue %}{% endif %}
+/**
+  {% if type.description %}
+ * {{type.description}}
+ *
+  {% endif %}
+ * @enum {string}
+ */
+chromium.DevTools.{{domain.domain}}.{{type.id}} = {
+  {% for literal in type.enum %}
+    {{ literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}: "{{ literal }}"{{',' if not loop.last}}
+  {% endfor %}
+};
+
+{% endfor %}
+
+{# Generate types. #}
+{% for type in domain.types %}
+  {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
+/**
+  {% if type.description %}
+ * {{type.description}}
+ *
+  {% endif %}
+  {% for property in type.properties %}
+    {% if property.description %}
+ * {{property.name}}: {{property.description}}
+    {% endif %}
+  {% endfor %}
+ *
+  {% if type.properties %}
+ * @typedef {{ '{{' }}
+  {% for property in type.properties %}
+    {% if property.optional %}
+ *   {{property.name}}: ({{ resolve_type(property).js_type }}|undefined){{',' if not loop.last}}
+    {% else %}
+ *   {{property.name}}: {{ resolve_type(property).js_type }}{{',' if not loop.last}}
+    {% endif %}
+  {% endfor %}
+ * {{ '}}' }}
+  {% else %}
+ * @typedef {undefined}
+  {% endif %}
+ */
+chromium.DevTools.{{domain.domain}}.{{type.id}};
+
+{% endfor %}
+
+{# Generate all commands for Experimental Domain. #}
+{% for command in domain.commands %}
+  {% set method_name = command.name | sanitize_literal %}
+  {% set title_case_method_name = method_name | to_title_case %}
+  {% set result_type = '{!Promise<chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
+
+/**
+  {% if command.description %}
+  * {{ command.description }}
+  *
+  {% endif %}
+  {% if command.parameters|length > 0 %}
+  * @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
+  {% endif %}
+  * @return {{result_type}}
+  */
+  {% if command.parameters|length > 0 %}
+chromium.DevTools.Experimental{{domain.domain}}.prototype.{{method_name}} = function(params) {};
+  {% else %}
+chromium.DevTools.Experimental{{domain.domain}}.prototype.{{method_name}} = function() {};
+  {% endif %}
+{% endfor %}
+
+{# Generate all events Experimental Domain. #}
+{% for event in domain.events %}
+  {% if event.parameters|length > 0 %}
+    {% set param_type = '{!function(!chromium.DevTools.' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
+  {% else %}
+    {% set param_type = '{!function()}' %}
+  {% endif %}
+
+/**
+  {% if event.description %}
+  * {{ event.description }}
+  *
+  {% endif %}
+  * @param {{param_type}} listener
+  * @return {number} The id of this event listener.
+  */
+chromium.DevTools.Experimental{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {};
+{% endfor %}
+
+
+{# Generate non-Experimental commands. #}
+{% for command in domain.commands %}
+  {% if command.Experimental %}{% continue %}{% endif %}
+  {% set method_name = command.name | sanitize_literal %}
+  {% set title_case_method_name = method_name | to_title_case %}
+  {% set result_type = '{!Promise<chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
+
+/**
+  {% if command.description %}
+  * {{ command.description }}
+  *
+  {% endif %}
+  {% if command.parameters|length > 0 %}
+  * @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
+  {% endif %}
+  * @return {{result_type}}
+  */
+  {% if command.parameters|length > 0 %}
+chromium.DevTools.{{domain.domain}}.prototype.{{method_name}} = function(params) {};
+  {% else %}
+chromium.DevTools.{{domain.domain}}.prototype.{{method_name}} = function() {};
+  {% endif %}
+{% endfor %}
+
+{# Generate non-Experimental events. #}
+{% for event in domain.events %}
+  {% if event.Experimental %}{% continue %}{% endif %}
+  {% if event.parameters|length > 0 %}
+    {% set param_type = '{!function(!chromium.DevTools.' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
+  {% else %}
+    {% set param_type = '{!function()}' %}
+  {% endif %}
+
+/**
+  {% if event.description %}
+  * {{ event.description }}
+  *
+  {% endif %}
+  * @param {{param_type}} listener
+  * @return {number} The id of this event listener.
+  */
+chromium.DevTools.{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {};
+{% endfor %}
diff --git a/headless/lib/browser/devtools_api/domain_js.template b/headless/lib/browser/devtools_api/domain_js.template
index d8bdc67..0b701ed 100644
--- a/headless/lib/browser/devtools_api/domain_js.template
+++ b/headless/lib/browser/devtools_api/domain_js.template
@@ -2,161 +2,75 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+{# Changes to the generated API must be reflected in the externs too. #}
+
 /**
  * @fileoverview Generated DevTools bindings for the {{domain.domain}} Domain.
- * Note bindings are not generated for experimental commands and events, if you
- * need to use one of those you must use the
- * <code>chromium.DevTools.Connection</code> class directly.
  */
 'use strict';
 
-goog.module('chromium.DevTools.{{domain.domain}}');
-const Connection = goog.require('chromium.DevTools.Connection');
+goog.provide('chromium.DevTools.Experimental{{domain.domain}}');
+goog.provide('chromium.DevTools.{{domain.domain}}');
+goog.require('chromium.DevTools.Connection');
 {% for domain_name in domain.js_dependencies %}
-const {{domain_name}} = goog.require('chromium.DevTools.{{domain_name}}');
+goog.require('chromium.DevTools.{{domain_name}}');
 {% endfor %}
 {% for forward_declaration in domain.js_forward_declarations %}
 goog.forwardDeclare('chromium.DevTools.{{forward_declaration}}');
 {% endfor %}
 
+goog.scope(function() {
+const Connection = chromium.DevTools.Connection;
+{% for domain_name in domain.js_dependencies %}
+const {{domain_name}} = chromium.DevTools.{{domain_name}};
+{% endfor %}
+
 /**
  * Experimental bindings for the {{domain.domain}} DevTools Domain.  Note these
  * are subject to change without warning. Use at your own risk.
+ * @param {!Connection} connection The DevTools connection.
+ * @constructor
  */
-class Experimental{{domain.domain}} {
-  /**
-   * @param {!Connection} connection The DevTools connection.
-   */
-  constructor(connection) {
-    /** @private {!Connection} */
-    this.connection_ = connection;
-  };
+chromium.DevTools.Experimental{{domain.domain}} = function(connection) {
+  /** @private {!Connection} */
+  this.connection_ = connection;
+}
 
-  /**
-   * Removes an event listener.
-   *
-   * @param {number} id The id of the event listener to remove.
-   * @return {boolean} Whether the event listener was actually removed.
-   */
-  removeEventListener(id) {
-    return this.connection_.removeEventListener(id);
-  }
+const Experimental{{domain.domain}} = chromium.DevTools.Experimental{{domain.domain}};
 
-{# Generate all commands. #}
-{% for command in domain.commands %}
-  {% set method_name = command.name | sanitize_literal %}
-  {% set title_case_method_name = method_name | to_title_case %}
-  {% if command.parameters|length > 0 %}
-    {% set param_name = 'params' %}
-    {% set param_type = '{' + domain.domain + '.' + title_case_method_name + 'Params}' %}
-  {% else %}
-    {% set param_name = 'opt_params' %}
-    {% set param_type = '{' + domain.domain + '.' + title_case_method_name + 'Params=}' %}
-  {% endif %}
-  {% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
-
-  /**
-    {% if command.description %}
-    * {{ command.description }}
-    {% endif %}
-    * @param {{param_type}} {{param_name}}
-    * @return {{result_type}}
-    */
-  {{method_name}}({{param_name}}) {
-    return /** @type {{result_type}} **/(
-        this.connection_.sendDevToolsMessage(
-            '{{domain.domain}}.{{command.name}}', {{param_name}}));
-  }
-{% endfor %}
-
-{# Generate all events. #}
-{% for event in domain.events %}
-  {% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
-
-  /**
-    {% if event.description %}
-    * {{ event.description }}
-    {% endif %}
-    * @param {{param_type}} listener
-    * @return {number} The id of this event listener.
-    */
-  on{{event.name | to_title_case}}(listener) {
-    return this.connection_.addEventListener(
-        '{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
-  }
-{% endfor %}
+/**
+ * Removes an event listener.
+ *
+ * @param {number} id The id of the event listener to remove.
+ * @return {boolean} Whether the event listener was actually removed.
+ */
+Experimental{{domain.domain}}.prototype.removeEventListener = function(id) {
+  return this.connection_.removeEventListener(id);
 }
 
 /**
  * Bindings for the {{domain.domain}} DevTools Domain.
+ * @param {!Connection} connection The DevTools connection.
+ * @constructor
  */
-class {{domain.domain}} {
-  /**
-   * @param {!Connection} connection The DevTools connection.
-   */
-  constructor(connection) {
-    /** @private {!Connection} */
-    this.connection_ = connection;
+chromium.DevTools.{{domain.domain}} = function(connection) {
+  /** @private {!Connection} */
+  this.connection_ = connection;
 
-    /** @type {!Experimental{{domain.domain}}} */
-    this.experimental = new Experimental{{domain.domain}}(connection);
-  };
+  /** @type {!Experimental{{domain.domain}}} */
+  this.experimental = new Experimental{{domain.domain}}(connection);
+}
 
-  /**
-   * Removes an event listener.
-   *
-   * @param {number} id The id of the event listener to remove.
-   * @return {boolean} Whether the event listener was actually removed.
-   */
-  removeEventListener(id) {
-    return this.connection_.removeEventListener(id);
-  }
+const {{domain.domain}} = chromium.DevTools.{{domain.domain}};
 
-{# Generate non-experimental commands. #}
-{% for command in domain.commands %}
-  {% if command.experimental %}{% continue %}{% endif %}
-  {% set method_name = command.name | sanitize_literal %}
-  {% set title_case_method_name = method_name | to_title_case %}
-  {% if command.parameters|length > 0 %}
-    {% set param_name = 'params' %}
-    {% set param_type = '{' + domain.domain + '.' + title_case_method_name + 'Params}' %}
-  {% else %}
-    {% set param_name = 'opt_params' %}
-    {% set param_type = '{' + domain.domain + '.' + title_case_method_name + 'Params=}' %}
-  {% endif %}
-  {% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
-
-  /**
-    {% if command.description %}
-    * {{ command.description }}
-    {% endif %}
-    * @param {{param_type}} {{param_name}}
-    * @return {{result_type}}
-    */
-  {{method_name}}({{param_name}}) {
-    return /** @type {{result_type}} **/(
-        this.connection_.sendDevToolsMessage(
-            '{{domain.domain}}.{{command.name}}', {{param_name}}));
-  }
-{% endfor %}
-
-{# Generate non-experimental events. #}
-{% for event in domain.events %}
-  {% if event.experimental %}{% continue %}{% endif %}
-  {% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
-
-  /**
-    {% if event.description %}
-    * {{ event.description }}
-    {% endif %}
-    * @param {{param_type}} listener
-    * @return {number} The id of this event listener.
-    */
-  on{{event.name | to_title_case}}(listener) {
-    return this.connection_.addEventListener(
-        '{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
-  }
-{% endfor %}
+/**
+ * Removes an event listener.
+ *
+ * @param {number} id The id of the event listener to remove.
+ * @return {boolean} Whether the event listener was actually removed.
+ */
+{{domain.domain}}.prototype.removeEventListener = function(id) {
+  return this.connection_.removeEventListener(id);
 }
 
 {# Generate enums. #}
@@ -207,7 +121,108 @@
  */
 {{domain.domain}}.{{type.id}};
 
-
 {% endfor %}
 
-exports = {{domain.domain}};
+{# Generate all commands for Experimental Domain. #}
+{% for command in domain.commands %}
+  {% set method_name = command.name | sanitize_literal %}
+  {% set title_case_method_name = method_name | to_title_case %}
+  {% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
+
+/**
+  {% if command.description %}
+  * {{ command.description }}
+  *
+  {% endif %}
+  {% if command.parameters|length > 0 %}
+  * @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
+  {% endif %}
+  * @return {{result_type}}
+  */
+  {% if command.parameters|length > 0 %}
+Experimental{{domain.domain}}.prototype.{{method_name}} = function(params) {
+  return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', params);
+};
+  {% else %}
+Experimental{{domain.domain}}.prototype.{{method_name}} = function() {
+  return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', {});
+};
+  {% endif %}
+{% endfor %}
+
+{# Generate all events Experimental Domain. #}
+{% for event in domain.events %}
+  {% if event.parameters|length > 0 %}
+    {% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
+  {% else %}
+    {% set param_type = '{!function()}' %}
+  {% endif %}
+
+/**
+  {% if event.description %}
+  * {{ event.description }}
+  *
+  {% endif %}
+  * @param {{param_type}} listener
+  * @return {number} The id of this event listener.
+  */
+Experimental{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {
+  return this.connection_.addEventListener(
+      '{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
+};
+{% endfor %}
+
+
+{# Generate non-Experimental commands. #}
+{% for command in domain.commands %}
+  {% if command.Experimental %}{% continue %}{% endif %}
+  {% set method_name = command.name | sanitize_literal %}
+  {% set title_case_method_name = method_name | to_title_case %}
+  {% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
+
+/**
+  {% if command.description %}
+  * {{ command.description }}
+  *
+  {% endif %}
+  {% if command.parameters|length > 0 %}
+  * @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
+  {% endif %}
+  * @return {{result_type}}
+  */
+  {% if command.parameters|length > 0 %}
+{{domain.domain}}.prototype.{{method_name}} = function(params) {
+  return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', params);
+};
+  {% else %}
+{{domain.domain}}.prototype.{{method_name}} = function() {
+  return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', {});
+};
+  {% endif %}
+{% endfor %}
+
+{# Generate non-Experimental events. #}
+{% for event in domain.events %}
+  {% if event.Experimental %}{% continue %}{% endif %}
+  {% if event.parameters|length > 0 %}
+    {% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
+  {% else %}
+    {% set param_type = '{!function()}' %}
+  {% endif %}
+
+/**
+  {% if event.description %}
+  * {{ event.description }}
+  *
+  {% endif %}
+  * @param {{param_type}} listener
+  * @return {number} The id of this event listener.
+  */
+{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {
+  return this.connection_.addEventListener(
+      '{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
+};
+{% endfor %}
+
+});  // goog.scope
+
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 03d2806c58..6ceff6a 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -1789,10 +1789,11 @@
       new ToolbarModelDelegateIOS([_model webStateList]));
   _toolbarModelIOS.reset([_dependencyFactory
       newToolbarModelIOSWithDelegate:_toolbarModelDelegate.get()]);
-  _toolbarController = [_dependencyFactory
-      newWebToolbarControllerWithDelegate:self
-                                urlLoader:self
-                          preloadProvider:_preloadController];
+  _toolbarController =
+      [_dependencyFactory newWebToolbarControllerWithDelegate:self
+                                                    urlLoader:self
+                                              preloadProvider:_preloadController
+                                                   dispatcher:self.dispatcher];
   [_dispatcher startDispatchingToTarget:_toolbarController
                             forProtocol:@protocol(OmniboxFocuser)];
   [_toolbarController setTabCount:[_model count]];
@@ -4020,6 +4021,16 @@
   return card;
 }
 
+#pragma mark - BrowserCommands
+
+- (void)goBack {
+  [[_model currentTab] goBack];
+}
+
+- (void)goForward {
+  [[_model currentTab] goForward];
+}
+
 #pragma mark - Command Handling
 
 - (IBAction)chromeExecuteCommand:(id)sender {
@@ -4030,9 +4041,6 @@
   Tab* currentTab = [_model currentTab];
 
   switch (command) {
-    case IDC_BACK:
-      [[_model currentTab] goBack];
-      break;
     case IDC_BOOKMARK_PAGE:
       [self initializeBookmarkInteractionController];
       [_bookmarkInteractionController
@@ -4068,9 +4076,6 @@
     case IDC_FIND_UPDATE:
       [self searchFindInPage];
       break;
-    case IDC_FORWARD:
-      [[_model currentTab] goForward];
-      break;
     case IDC_FULLSCREEN:
       NOTIMPLEMENTED();
       break;
diff --git a/ios/chrome/browser/ui/browser_view_controller_dependency_factory.h b/ios/chrome/browser/ui/browser_view_controller_dependency_factory.h
index e38cefe..7b57a2d 100644
--- a/ios/chrome/browser/ui/browser_view_controller_dependency_factory.h
+++ b/ios/chrome/browser/ui/browser_view_controller_dependency_factory.h
@@ -10,6 +10,7 @@
 #include "ios/chrome/browser/ui/tabs/tab_strip_controller.h"
 
 @class AlertCoordinator;
+@protocol BrowserCommands;
 @class KeyCommandsProvider;
 @class MessageBubbleView;
 @class PKPass;
@@ -65,7 +66,8 @@
 - (WebToolbarController*)
 newWebToolbarControllerWithDelegate:(id<WebToolbarDelegate>)delegate
                           urlLoader:(id<UrlLoader>)urlLoader
-                    preloadProvider:(id<PreloadProvider>)preload;
+                    preloadProvider:(id<PreloadProvider>)preload
+                         dispatcher:(id<BrowserCommands>)dispatcher;
 
 // Returns a new keyboard commands coordinator to handle keyboard commands.
 - (KeyCommandsProvider*)newKeyCommandsProvider;
diff --git a/ios/chrome/browser/ui/browser_view_controller_dependency_factory.mm b/ios/chrome/browser/ui/browser_view_controller_dependency_factory.mm
index 3708124..36a34f2 100644
--- a/ios/chrome/browser/ui/browser_view_controller_dependency_factory.mm
+++ b/ios/chrome/browser/ui/browser_view_controller_dependency_factory.mm
@@ -81,11 +81,13 @@
 - (WebToolbarController*)
 newWebToolbarControllerWithDelegate:(id<WebToolbarDelegate>)delegate
                           urlLoader:(id<UrlLoader>)urlLoader
-                    preloadProvider:(id<PreloadProvider>)preload {
+                    preloadProvider:(id<PreloadProvider>)preload
+                         dispatcher:(id<BrowserCommands>)dispatcher {
   return [[WebToolbarController alloc] initWithDelegate:delegate
                                               urlLoader:urlLoader
                                            browserState:browserState_
-                                        preloadProvider:preload];
+                                        preloadProvider:preload
+                                             dispatcher:dispatcher];
 }
 
 - (KeyCommandsProvider*)newKeyCommandsProvider {
diff --git a/ios/chrome/browser/ui/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
index 11d3e6ee..72f2e73 100644
--- a/ios/chrome/browser/ui/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
@@ -236,7 +236,8 @@
     [[[factory stub] andReturn:nil]
         newWebToolbarControllerWithDelegate:[OCMArg any]
                                   urlLoader:[OCMArg any]
-                            preloadProvider:[OCMArg any]];
+                            preloadProvider:[OCMArg any]
+                                 dispatcher:[OCMArg any]];
     [[[factory stub] andReturn:shareController_] shareControllerInstance];
     [[[factory stub] andReturn:passKitViewController_]
         newPassKitViewControllerForPass:nil];
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index 94d30e1e..ee911d0 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -9,9 +9,15 @@
 // which in practice is the BrowserViewController instance displaying the tab.
 @protocol BrowserCommands
 
-// Close the current tab.
+// Closes the current tab.
 - (void)closeCurrentTab;
 
+// Navigates backwards in the current tab's history.
+- (void)goBack;
+
+// Navigates forwards in the current tab's history.
+- (void)goForward;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/ios_command_ids.h b/ios/chrome/browser/ui/commands/ios_command_ids.h
index 2a2d01b..58a4aa3 100644
--- a/ios/chrome/browser/ui/commands/ios_command_ids.h
+++ b/ios/chrome/browser/ui/commands/ios_command_ids.h
@@ -13,8 +13,6 @@
 // also need to be updated.
 
 // clang-format off
-#define IDC_BACK                                       33000
-#define IDC_FORWARD                                    33001
 #define IDC_RELOAD                                     33002
 #define IDC_STOP                                       33006
 #define IDC_NEW_TAB                                    34014
diff --git a/ios/chrome/browser/ui/key_commands_provider.mm b/ios/chrome/browser/ui/key_commands_provider.mm
index ffabada..e09cb7c 100644
--- a/ios/chrome/browser/ui/key_commands_provider.mm
+++ b/ios/chrome/browser/ui/key_commands_provider.mm
@@ -39,22 +39,36 @@
   const BOOL hasTabs = [consumer tabsCount] > 0;
 
   const BOOL useRTLLayout = UseRTLLayout();
-  const NSInteger browseLeft = useRTLLayout ? IDC_FORWARD : IDC_BACK;
-  const NSInteger browseRight = useRTLLayout ? IDC_BACK : IDC_FORWARD;
+
+  // Blocks for navigating forward/back.
+  void (^browseLeft)();
+  void (^browseRight)();
+  if (useRTLLayout) {
+    browseLeft = ^{
+      if ([weakConsumer canGoForward])
+        [weakDispatcher goForward];
+    };
+    browseRight = ^{
+      if ([weakConsumer canGoBack])
+        [weakDispatcher goBack];
+    };
+  } else {
+    browseLeft = ^{
+      if ([weakConsumer canGoBack])
+        [weakDispatcher goBack];
+    };
+    browseRight = ^{
+      if ([weakConsumer canGoForward])
+        [weakDispatcher goForward];
+    };
+  }
+
   const int browseLeftDescriptionID = useRTLLayout
                                           ? IDS_IOS_KEYBOARD_HISTORY_FORWARD
                                           : IDS_IOS_KEYBOARD_HISTORY_BACK;
   const int browseRightDescriptionID = useRTLLayout
                                            ? IDS_IOS_KEYBOARD_HISTORY_BACK
                                            : IDS_IOS_KEYBOARD_HISTORY_FORWARD;
-  BOOL (^canBrowseLeft)() = ^() {
-    return useRTLLayout ? [weakConsumer canGoForward]
-                        : [weakConsumer canGoBack];
-  };
-  BOOL (^canBrowseRight)() = ^() {
-    return useRTLLayout ? [weakConsumer canGoBack]
-                        : [weakConsumer canGoForward];
-  };
 
   // Initialize the array of commands with an estimated capacity.
   NSMutableArray* keyCommands = [NSMutableArray arrayWithCapacity:32];
@@ -155,18 +169,14 @@
                                        title:l10n_util::GetNSStringWithFixup(
                                                  browseLeftDescriptionID)
                                       action:^{
-                                        if (canBrowseLeft()) {
-                                          execute(browseLeft);
-                                        }
+                                        browseLeft();
                                       }],
         [UIKeyCommand cr_keyCommandWithInput:UIKeyInputRightArrow
                                modifierFlags:UIKeyModifierCommand
                                        title:l10n_util::GetNSStringWithFixup(
                                                  browseRightDescriptionID)
                                       action:^{
-                                        if (canBrowseRight()) {
-                                          execute(browseRight);
-                                        }
+                                        browseRight();
                                       }],
       ]];
     }
@@ -225,17 +235,13 @@
                              modifierFlags:UIKeyModifierCommand
                                      title:nil
                                     action:^{
-                                      if (canBrowseLeft()) {
-                                        execute(browseLeft);
-                                      }
+                                      browseLeft();
                                     }],
       [UIKeyCommand cr_keyCommandWithInput:@"]"
                              modifierFlags:UIKeyModifierCommand
                                      title:nil
                                     action:^{
-                                      if (canBrowseRight()) {
-                                        execute(browseRight);
-                                      }
+                                      browseRight();
                                     }],
       [UIKeyCommand cr_keyCommandWithInput:@"."
                              modifierFlags:UIKeyModifierCommand
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
index 0381854..13e228c 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
@@ -70,8 +70,8 @@
   DCHECK(!_toolbarController);
   DCHECK(readingListModel);
 
-  _toolbarController = [[NewTabPageToolbarController alloc] init];
-  [_toolbarController setDispatcher:dispatcher];
+  _toolbarController =
+      [[NewTabPageToolbarController alloc] initWithDispatcher:dispatcher];
   _toolbarController.readingListModel = readingListModel;
 
   UIView* toolbarView = [_toolbarController view];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h
index 8e90625..58533816 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h
@@ -7,9 +7,10 @@
 
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
 
+@protocol BrowserCommands;
 @protocol GoogleLandingDataSource;
-@protocol UrlLoader;
 @protocol OmniboxFocuser;
+@protocol UrlLoader;
 @protocol WebToolbarDelegate;
 
 // New tab page specific toolbar. The background view is hidden and the
@@ -20,10 +21,13 @@
 
 // Designated initializer. The underlying ToolbarController is initialized with
 // ToolbarControllerStyleLightMode.
-- (instancetype)init;
+- (instancetype)initWithDispatcher:
+    (id<BrowserCommands, OmniboxFocuser, UrlLoader, WebToolbarDelegate>)
+        dispatcher;
 
-@property(nonatomic, weak) id<UrlLoader, OmniboxFocuser, WebToolbarDelegate>
-    dispatcher;
+@property(nonatomic, readonly, weak)
+    id<BrowserCommands, OmniboxFocuser, UrlLoader, WebToolbarDelegate>
+        dispatcher;
 
 // |YES| if the toolbar can show the forward arrow.
 - (void)setCanGoForward:(BOOL)canGoForward;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
index deeb6f8..7d601fa 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
@@ -62,8 +62,11 @@
 @synthesize canGoForward = _canGoForward;
 @synthesize canGoBack = _canGoBack;
 
-- (instancetype)init {
-  self = [super initWithStyle:ToolbarControllerStyleLightMode];
+- (instancetype)initWithDispatcher:
+    (id<BrowserCommands, OmniboxFocuser, UrlLoader, WebToolbarDelegate>)
+        dispatcher {
+  self = [super initWithStyle:ToolbarControllerStyleLightMode
+                   dispatcher:dispatcher];
   if (self) {
     [self.backgroundView setHidden:YES];
 
@@ -125,13 +128,18 @@
             initWithTarget:self
                     action:@selector(handleLongPress:)];
     [_backButton addGestureRecognizer:backLongPress];
+    [_backButton addTarget:self.dispatcher
+                    action:@selector(goBack)
+          forControlEvents:UIControlEventTouchUpInside];
+
     UILongPressGestureRecognizer* forwardLongPress =
         [[UILongPressGestureRecognizer alloc]
             initWithTarget:self
                     action:@selector(handleLongPress:)];
     [_forwardButton addGestureRecognizer:forwardLongPress];
-    [_backButton setTag:IDC_BACK];
-    [_forwardButton setTag:IDC_FORWARD];
+    [_forwardButton addTarget:self.dispatcher
+                       action:@selector(goForward)
+             forControlEvents:UIControlEventTouchUpInside];
 
     [_omniboxFocuser addTarget:self
                         action:@selector(focusOmnibox:)
diff --git a/ios/chrome/browser/ui/omnibox_perftest.mm b/ios/chrome/browser/ui/omnibox_perftest.mm
index 66538de..3f76fa3 100644
--- a/ios/chrome/browser/ui/omnibox_perftest.mm
+++ b/ios/chrome/browser/ui/omnibox_perftest.mm
@@ -107,7 +107,8 @@
         initWithDelegate:webToolbarDelegate
                urlLoader:urlLoader
             browserState:chrome_browser_state_.get()
-         preloadProvider:nil];
+         preloadProvider:nil
+              dispatcher:nil];
     UIView* toolbarView = [toolbar_ view];
     CGRect toolbarFrame = toolbarView.frame;
     toolbarFrame.origin = CGPointZero;
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_toolbar_controller.mm b/ios/chrome/browser/ui/stack_view/stack_view_toolbar_controller.mm
index 8960e4f9..357dd92 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/stack_view/stack_view_toolbar_controller.mm
@@ -30,7 +30,7 @@
 }
 
 - (instancetype)initWithStackViewToolbar {
-  self = [super initWithStyle:ToolbarControllerStyleDarkMode];
+  self = [super initWithStyle:ToolbarControllerStyleDarkMode dispatcher:nil];
   if (self) {
     _stackViewToolbar =
         [[UIView alloc] initWithFrame:[self specificControlsArea]];
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller.h b/ios/chrome/browser/ui/toolbar/toolbar_controller.h
index fd9c388..d85fca6 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller.h
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller.h
@@ -18,6 +18,7 @@
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/util/relaxed_bounds_constraints_hittest.h"
 
+@protocol BrowserCommands;
 class ReadingListModel;
 @class ToolsMenuConfiguration;
 
@@ -134,8 +135,15 @@
 // The reading list model reflected by the toolbar.
 @property(nonatomic, readwrite, assign) ReadingListModel* readingListModel;
 
-// Designated initializer.  |style| determines how the toolbar draws itself.
+// The command dispatcher this and any subordinate objects should use.
+@property(nonatomic, readonly, weak) id<BrowserCommands> dispatcher;
+
+// Designated initializer.
+//   |style| determines how the toolbar draws itself.
+//   |dispatcher| is is the dispatcher for calling methods handled in other
+//     parts of the app.
 - (instancetype)initWithStyle:(ToolbarControllerStyle)style
+                   dispatcher:(id<BrowserCommands>)dispatcher
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
index 9ceeae4..d3cdf8d1 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
@@ -249,6 +249,7 @@
 @synthesize shadowView = shadowView_;
 @synthesize toolsPopupController = toolsPopupController_;
 @synthesize style = style_;
+@synthesize dispatcher = dispatcher_;
 
 - (void)setReadingListModel:(ReadingListModel*)readingListModel {
   readingListModel_ = readingListModel;
@@ -259,10 +260,12 @@
   }
 }
 
-- (instancetype)initWithStyle:(ToolbarControllerStyle)style {
+- (instancetype)initWithStyle:(ToolbarControllerStyle)style
+                   dispatcher:(id<BrowserCommands>)dispatcher {
   self = [super init];
   if (self) {
     style_ = style;
+    dispatcher_ = dispatcher;
     DCHECK_LT(style_, ToolbarControllerStyleMaxStyles);
 
     InterfaceIdiom idiom = IsIPadIdiom() ? IPAD_IDIOM : IPHONE_IDIOM;
@@ -558,9 +561,12 @@
   [button addTarget:self
                 action:@selector(recordUserMetrics:)
       forControlEvents:UIControlEventTouchUpInside];
-  [button addTarget:button
-                action:@selector(chromeExecuteCommand:)
-      forControlEvents:UIControlEventTouchUpInside];
+  // Only register buttons with defined tags for -chromeExecuteCommand:.
+  if (button.tag) {
+    [button addTarget:button
+                  action:@selector(chromeExecuteCommand:)
+        forControlEvents:UIControlEventTouchUpInside];
+  }
 }
 
 - (CGRect)shareButtonAnchorRect {
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller_unittest.mm b/ios/chrome/browser/ui/toolbar/toolbar_controller_unittest.mm
index 06979e0..6064f5c 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller_unittest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller_unittest.mm
@@ -35,8 +35,9 @@
  protected:
   void SetUp() override {
     PlatformTest::SetUp();
-    toolbarController_ = [[ToolbarController alloc]
-        initWithStyle:ToolbarControllerStyleLightMode];
+    toolbarController_ =
+        [[ToolbarController alloc] initWithStyle:ToolbarControllerStyleLightMode
+                                      dispatcher:nil];
   }
 
   ToolbarController* toolbarController_;
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
index 38f3812..4ae4ce5 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
@@ -13,6 +13,7 @@
 #include "ios/public/provider/chrome/browser/voice/voice_search_controller_delegate.h"
 #include "ios/web/public/navigation_item_list.h"
 
+@protocol BrowserCommands;
 @protocol PreloadProvider;
 @class Tab;
 @protocol ToolbarFrameDelegate;
@@ -88,7 +89,8 @@
 @property(nonatomic, weak, readonly) id<UrlLoader> urlLoader;
 
 // Mark inherited initializer as unavailable.
-- (instancetype)initWithStyle:(ToolbarControllerStyle)style NS_UNAVAILABLE;
+- (instancetype)initWithStyle:(ToolbarControllerStyle)style
+                   dispatcher:(id<BrowserCommands>)dispatcher NS_UNAVAILABLE;
 
 // Create a new web toolbar controller whose omnibox is backed by
 // |browserState|.
@@ -96,6 +98,7 @@
                        urlLoader:(id<UrlLoader>)urlLoader
                     browserState:(ios::ChromeBrowserState*)browserState
                  preloadProvider:(id<PreloadProvider>)preloader
+                      dispatcher:(id<BrowserCommands>)dispatcher
     NS_DESIGNATED_INITIALIZER;
 
 // Called when the browser state this object was initialized with is being
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
index bedfe15b..d29d4d4 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
@@ -37,6 +37,7 @@
 #import "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/history/tab_history_popup_controller.h"
@@ -369,7 +370,8 @@
 - (instancetype)initWithDelegate:(id<WebToolbarDelegate>)delegate
                        urlLoader:(id<UrlLoader>)urlLoader
                     browserState:(ios::ChromeBrowserState*)browserState
-                 preloadProvider:(id<PreloadProvider>)preloader {
+                 preloadProvider:(id<PreloadProvider>)preloader
+                      dispatcher:(id<BrowserCommands>)dispatcher {
   DCHECK(delegate);
   DCHECK(urlLoader);
   DCHECK(browserState);
@@ -377,8 +379,10 @@
   _urlLoader = urlLoader;
   _browserState = browserState;
   _incognito = browserState->IsOffTheRecord();
-  self = [super initWithStyle:(_incognito ? ToolbarControllerStyleIncognitoMode
-                                          : ToolbarControllerStyleLightMode)];
+  ToolbarControllerStyle style =
+      (_incognito ? ToolbarControllerStyleIncognitoMode
+                  : ToolbarControllerStyleLightMode);
+  self = [super initWithStyle:style dispatcher:dispatcher];
   if (!self)
     return nil;
 
@@ -501,6 +505,14 @@
     [_voiceSearchButton
         setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin |
                             UIViewAutoresizingFlexibleLeadingMargin()];
+
+    // Assign tags before calling -setUpButton, since only buttons with tags
+    // have -chromeExecuteCommand added as a target.
+    [_reloadButton setTag:IDC_RELOAD];
+    [_stopButton setTag:IDC_STOP];
+    [_starButton setTag:IDC_BOOKMARK_PAGE];
+    [_voiceSearchButton setTag:IDC_VOICE_SEARCH];
+
     [_webToolbar addSubview:_voiceSearchButton];
     [_webToolbar addSubview:_starButton];
     [_webToolbar addSubview:_stopButton];
@@ -542,6 +554,14 @@
       hasDisabledImage:YES
          synchronously:NO];
 
+  // Assign targets for buttons using the dispatcher.
+  [_backButton addTarget:self.dispatcher
+                  action:@selector(goBack)
+        forControlEvents:UIControlEventTouchUpInside];
+  [_forwardButton addTarget:self.dispatcher
+                     action:@selector(goForward)
+           forControlEvents:UIControlEventTouchUpInside];
+
   _backButtonMode = ToolbarButtonModeNormal;
   _forwardButtonMode = ToolbarButtonModeNormal;
   UILongPressGestureRecognizer* backLongPress =
@@ -569,13 +589,6 @@
                   action:@selector(cancelOmniboxEdit)
         forControlEvents:UIControlEventTouchUpInside];
 
-  [_backButton setTag:IDC_BACK];
-  [_forwardButton setTag:IDC_FORWARD];
-  [_reloadButton setTag:IDC_RELOAD];
-  [_stopButton setTag:IDC_STOP];
-  [_starButton setTag:IDC_BOOKMARK_PAGE];
-  [_voiceSearchButton setTag:IDC_VOICE_SEARCH];
-
   SetA11yLabelAndUiAutomationName(_backButton, IDS_ACCNAME_BACK, @"Back");
   SetA11yLabelAndUiAutomationName(_forwardButton, IDS_ACCNAME_FORWARD,
                                   @"Forward");
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller_unittest.mm b/ios/chrome/browser/ui/toolbar/web_toolbar_controller_unittest.mm
index 500e4b24..163ecf7 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller_unittest.mm
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller_unittest.mm
@@ -77,7 +77,8 @@
         initWithDelegate:delegate
                urlLoader:urlLoader
             browserState:chrome_browser_state_.get()
-         preloadProvider:nil];
+         preloadProvider:nil
+              dispatcher:nil];
     [web_toolbar_controller_ setUnitTesting:YES];
   }
   void TearDown() override {
diff --git a/ios/chrome/browser/web/cache_egtest.mm b/ios/chrome/browser/web/cache_egtest.mm
index 75ed8f5..60805bd 100644
--- a/ios/chrome/browser/web/cache_egtest.mm
+++ b/ios/chrome/browser/web/cache_egtest.mm
@@ -169,9 +169,7 @@
 // Navigates back to the previous webpage.
 // TODO(crbug.com/638674): Evaluate if this can move to shared code.
 - (void)goBack {
-  GenericChromeCommand* backCommand =
-      [[GenericChromeCommand alloc] initWithTag:IDC_BACK];
-  chrome_test_util::RunCommandWithActiveViewController(backCommand);
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() goBack];
 
   [ChromeEarlGrey waitForPageToFinishLoading];
 }
diff --git a/ios/chrome/browser/web/visible_url_egtest.mm b/ios/chrome/browser/web/visible_url_egtest.mm
index c83999d..70ac2a1 100644
--- a/ios/chrome/browser/web/visible_url_egtest.mm
+++ b/ios/chrome/browser/web/visible_url_egtest.mm
@@ -519,12 +519,9 @@
       performAction:grey_tap()];
   [ChromeEarlGrey waitForWebViewContainingText:kTestPage1];
 
-  // Quickly (using chrome command) navigate forward twice and wait for
-  // kChromeUIVersionURL to load.
-  GenericChromeCommand* forwardCommand =
-      [[GenericChromeCommand alloc] initWithTag:IDC_FORWARD];
-  chrome_test_util::RunCommandWithActiveViewController(forwardCommand);
-  chrome_test_util::RunCommandWithActiveViewController(forwardCommand);
+  // Quickly navigate forward twice and wait for kChromeUIVersionURL to load.
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() goForward];
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() goForward];
 
   const std::string version = version_info::GetVersionNumber();
   [ChromeEarlGrey waitForWebViewContainingText:version];
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 2eff22544..c0aba2f 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -124,16 +124,14 @@
 }
 
 + (void)goBack {
-  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_BACK]);
-  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() goBack];
+
   [ChromeEarlGrey waitForPageToFinishLoading];
 }
 
 + (void)goForward {
-  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_FORWARD]);
-  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() goForward];
+
   [ChromeEarlGrey waitForPageToFinishLoading];
 }
 
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 5436d6f3..bd12c8a 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -594,6 +594,13 @@
     "base/vector_math.h",
     "base/vector_math_testing.h",
   ]
+  if (is_mac) {
+    # These need to be included here because audio_latency.cc depends on them.
+    sources += [
+      "base/mac/audio_latency_mac.cc",
+      "base/mac/audio_latency_mac.h",
+    ]
+  }
   configs += [
     ":media_config",
     ":media_implementation",
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc
index 904ee12..8e45f5e 100644
--- a/media/audio/audio_manager_unittest.cc
+++ b/media/audio/audio_manager_unittest.cc
@@ -26,6 +26,7 @@
 #include "media/audio/fake_audio_log_factory.h"
 #include "media/audio/fake_audio_manager.h"
 #include "media/audio/test_audio_thread.h"
+#include "media/base/limits.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,6 +34,11 @@
 #include "media/audio/alsa/audio_manager_alsa.h"
 #endif  // defined(USE_ALSA)
 
+#if defined(OS_MACOSX)
+#include "media/audio/mac/audio_manager_mac.h"
+#include "media/base/mac/audio_latency_mac.h"
+#endif
+
 #if defined(OS_WIN)
 #include "base/win/scoped_com_initializer.h"
 #include "media/audio/win/audio_manager_win.h"
@@ -677,4 +683,97 @@
   audio_manager_->DisableOutputDebugRecording();
 }
 
+#if defined(OS_MACOSX) || defined(USE_CRAS)
+class TestAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
+ public:
+  TestAudioSourceCallback(int expected_frames_per_buffer,
+                          base::WaitableEvent* event)
+      : expected_frames_per_buffer_(expected_frames_per_buffer),
+        event_(event){};
+  ~TestAudioSourceCallback() override{};
+
+  int OnMoreData(base::TimeDelta,
+                 base::TimeTicks,
+                 int,
+                 AudioBus* dest) override {
+    EXPECT_EQ(dest->frames(), expected_frames_per_buffer_);
+    event_->Signal();
+    return 0;
+  }
+
+  void OnError() override { FAIL(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestAudioSourceCallback);
+
+  const int expected_frames_per_buffer_;
+  base::WaitableEvent* event_;
+};
+
+// Test that we can create an AudioOutputStream with kMinAudioBufferSize and
+// kMaxAudioBufferSize and that the callback AudioBus is the expected size.
+TEST_F(AudioManagerTest, CheckMinMaxAudioBufferSizeCallbacks) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+#if defined(OS_MACOSX)
+  CreateAudioManagerForTesting<AudioManagerMac>();
+#elif defined(USE_CRAS)
+  CreateAudioManagerForTesting<AudioManagerCras>();
+#endif
+
+  DCHECK(audio_manager_);
+
+  AudioParameters default_params;
+  GetDefaultOutputStreamParameters(&default_params);
+  ASSERT_LT(default_params.frames_per_buffer(),
+            media::limits::kMaxAudioBufferSize);
+
+#if defined(OS_MACOSX)
+  // On OSX the preferred output buffer size is higher than the minimum
+  // but users may request the minimum size explicitly.
+  ASSERT_GT(default_params.frames_per_buffer(),
+            GetMinAudioBufferSizeMacOS(media::limits::kMinAudioBufferSize,
+                                       default_params.sample_rate()));
+#elif defined(USE_CRAS)
+  // On CRAS the preferred output buffer size varies per board and may be as low
+  // as the minimum for some boards.
+  ASSERT_GE(default_params.frames_per_buffer(),
+            media::limits::kMinAudioBufferSize);
+#else
+  NOTREACHED();
+#endif
+
+  AudioOutputStream* stream;
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  // Create an output stream with the minimum buffer size parameters and ensure
+  // that no errors are returned.
+  AudioParameters min_params = default_params;
+  min_params.set_frames_per_buffer(media::limits::kMinAudioBufferSize);
+  stream = audio_manager_->MakeAudioOutputStreamProxy(min_params, "");
+  ASSERT_TRUE(stream);
+  EXPECT_TRUE(stream->Open());
+  event.Reset();
+  TestAudioSourceCallback min_source(min_params.frames_per_buffer(), &event);
+  stream->Start(&min_source);
+  event.Wait();
+  stream->Stop();
+  stream->Close();
+
+  // Verify the same for the maximum buffer size.
+  AudioParameters max_params = default_params;
+  max_params.set_frames_per_buffer(media::limits::kMaxAudioBufferSize);
+  stream = audio_manager_->MakeAudioOutputStreamProxy(max_params, "");
+  ASSERT_TRUE(stream);
+  EXPECT_TRUE(stream->Open());
+  event.Reset();
+  TestAudioSourceCallback max_source(max_params.frames_per_buffer(), &event);
+  stream->Start(&max_source);
+  event.Wait();
+  stream->Stop();
+  stream->Close();
+}
+#endif
+
 }  // namespace media
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index 2e6d346..f3beab2 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -24,6 +24,7 @@
 #include "media/audio/cras/cras_input.h"
 #include "media/audio/cras/cras_unified.h"
 #include "media/base/channel_layout.h"
+#include "media/base/limits.h"
 #include "media/base/localized_strings.h"
 
 // cras_util.h headers pull in min/max macros...
@@ -40,10 +41,6 @@
 // Default sample rate for input and output streams.
 const int kDefaultSampleRate = 48000;
 
-// Define bounds for the output buffer size.
-const int kMinimumOutputBufferSize = 512;
-const int kMaximumOutputBufferSize = 8192;
-
 // Default input buffer size.
 const int kDefaultInputBufferSize = 1024;
 
@@ -308,7 +305,7 @@
     return 768;
   else if (board == "samus")
     return 256;
-  return kMinimumOutputBufferSize;
+  return 512;
 }
 
 AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters(
@@ -323,8 +320,9 @@
     bits_per_sample = input_params.bits_per_sample();
     channel_layout = input_params.channel_layout();
     buffer_size =
-        std::min(kMaximumOutputBufferSize,
-                 std::max(buffer_size, input_params.frames_per_buffer()));
+        std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                 std::max(static_cast<int>(limits::kMinAudioBufferSize),
+                          input_params.frames_per_buffer()));
   }
 
   int user_buffer_size = GetUserBufferSize();
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc
index 09b6433..8478e0f 100644
--- a/media/audio/mac/audio_manager_mac.cc
+++ b/media/audio/mac/audio_manager_mac.cc
@@ -27,6 +27,7 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/channel_layout.h"
 #include "media/base/limits.h"
+#include "media/base/mac/audio_latency_mac.h"
 #include "media/base/media_switches.h"
 
 namespace media {
@@ -34,10 +35,6 @@
 // Maximum number of output streams that can be open simultaneously.
 static const int kMaxOutputStreams = 50;
 
-// Define bounds for for low-latency input and output streams.
-static const int kMinimumInputOutputBufferSize = 128;
-static const int kMaximumInputOutputBufferSize = 4096;
-
 // Default sample-rate on most Apple hardware.
 static const int kFallbackSampleRate = 44100;
 
@@ -836,11 +833,17 @@
   // Allow pass through buffer sizes.  If concurrent input and output streams
   // exist, they will use the smallest buffer size amongst them.  As such, each
   // stream must be able to FIFO requests appropriately when this happens.
-  int buffer_size = ChooseBufferSize(false, hardware_sample_rate);
+  int buffer_size;
   if (has_valid_input_params) {
+    // If passed in via the input_params we allow buffer sizes to go as
+    // low as the the kMinAudioBufferSize, ignoring what
+    // ChooseBufferSize() normally returns.
     buffer_size =
-        std::min(kMaximumInputOutputBufferSize,
-                 std::max(input_params.frames_per_buffer(), buffer_size));
+        std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                 std::max(input_params.frames_per_buffer(),
+                          static_cast<int>(limits::kMinAudioBufferSize)));
+  } else {
+    buffer_size = ChooseBufferSize(false, hardware_sample_rate);
   }
 
   int hardware_channels;
@@ -887,7 +890,7 @@
 }
 
 int AudioManagerMac::ChooseBufferSize(bool is_input, int sample_rate) {
-  // kMinimumInputOutputBufferSize is too small for the output side because
+  // kMinAudioBufferSize is too small for the output side because
   // CoreAudio can get into under-run if the renderer fails delivering data
   // to the browser within the allowed time by the OS. The workaround is to
   // use 256 samples as the default output buffer size for sample rates
@@ -895,20 +898,12 @@
   // TODO(xians): Remove this workaround after WebAudio supports user defined
   // buffer size.  See https://github.com/WebAudio/web-audio-api/issues/348
   // for details.
-  int buffer_size = is_input ?
-      kMinimumInputOutputBufferSize : 2 * kMinimumInputOutputBufferSize;
+  int buffer_size =
+      is_input ? limits::kMinAudioBufferSize : 2 * limits::kMinAudioBufferSize;
   const int user_buffer_size = GetUserBufferSize();
-  if (user_buffer_size) {
-    buffer_size = user_buffer_size;
-  } else if (sample_rate > 48000) {
-    // The default buffer size is too small for higher sample rates and may lead
-    // to glitching.  Adjust upwards by multiples of the default size.
-    if (sample_rate <= 96000)
-      buffer_size = 2 * kMinimumInputOutputBufferSize;
-    else if (sample_rate <= 192000)
-      buffer_size = 4 * kMinimumInputOutputBufferSize;
-  }
-
+  buffer_size = user_buffer_size
+                    ? user_buffer_size
+                    : GetMinAudioBufferSizeMacOS(buffer_size, sample_rate);
   return buffer_size;
 }
 
diff --git a/media/base/audio_latency.cc b/media/base/audio_latency.cc
index 61b3724..3c657a4 100644
--- a/media/base/audio_latency.cc
+++ b/media/base/audio_latency.cc
@@ -11,6 +11,11 @@
 #include "base/logging.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "media/base/limits.h"
+
+#if defined(OS_MACOSX)
+#include "media/base/mac/audio_latency_mac.h"
+#endif
 
 namespace media {
 
@@ -130,16 +135,32 @@
 int AudioLatency::GetExactBufferSize(base::TimeDelta duration,
                                      int sample_rate,
                                      int hardware_buffer_size) {
-  const double requested_buffer_size = duration.InSecondsF() * sample_rate;
-
   DCHECK_NE(0, hardware_buffer_size);
 
+// Other platforms do not currently support custom buffer sizes.
+#if !defined(OS_MACOSX) && !defined(USE_CRAS)
+  return hardware_buffer_size;
+#else
+  const double requested_buffer_size = duration.InSecondsF() * sample_rate;
+  int minimum_buffer_size = hardware_buffer_size;
+
+// On OSX and CRAS the preferred buffer size is larger than the minimum,
+// however we allow values down to the minimum if requested explicitly.
+#if defined(OS_MACOSX)
+  minimum_buffer_size =
+      GetMinAudioBufferSizeMacOS(limits::kMinAudioBufferSize, sample_rate);
+#elif defined(USE_CRAS)
+  minimum_buffer_size = limits::kMinAudioBufferSize;
+#endif
+
   // Round the requested size to the nearest multiple of the hardware size
   const int buffer_size =
       std::round(std::max(requested_buffer_size, 1.0) / hardware_buffer_size) *
       hardware_buffer_size;
 
-  return std::max(buffer_size, hardware_buffer_size);
+  return std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                  std::max(buffer_size, minimum_buffer_size));
+#endif
 }
 
 }  // namespace media
diff --git a/media/base/limits.h b/media/base/limits.h
index f936c8b..2a73c59 100644
--- a/media/base/limits.h
+++ b/media/base/limits.h
@@ -7,6 +7,8 @@
 #ifndef MEDIA_BASE_LIMITS_H_
 #define MEDIA_BASE_LIMITS_H_
 
+#include "build/build_config.h"
+
 namespace media {
 
 namespace limits {
@@ -58,6 +60,17 @@
   kMaxInitDataLength = 64 * 1024,         // 64 KB
   kMaxSessionResponseLength = 64 * 1024,  // 64 KB
   kMaxKeySystemLength = 256,
+
+// Minimum and maximum buffer sizes for certain audio platforms.
+#if defined(OS_MACOSX)
+  kMinAudioBufferSize = 128,
+  kMaxAudioBufferSize = 4096,
+#elif defined(USE_CRAS)
+  // Though CRAS has different per-board defaults, allow explicitly requesting
+  // this buffer size on any board.
+  kMinAudioBufferSize = 256,
+  kMaxAudioBufferSize = 8192,
+#endif
 };
 
 }  // namespace limits
diff --git a/media/base/mac/audio_latency_mac.cc b/media/base/mac/audio_latency_mac.cc
new file mode 100644
index 0000000..223e3ad8
--- /dev/null
+++ b/media/base/mac/audio_latency_mac.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/mac/audio_latency_mac.h"
+#include "media/base/limits.h"
+
+namespace media {
+
+int GetMinAudioBufferSizeMacOS(int min_buffer_size, int sample_rate) {
+  int buffer_size = min_buffer_size;
+  if (sample_rate > 48000) {
+    // The default buffer size is too small for higher sample rates and may lead
+    // to glitching.  Adjust upwards by multiples of the default size.
+    if (sample_rate <= 96000)
+      buffer_size = 2 * limits::kMinAudioBufferSize;
+    else if (sample_rate <= 192000)
+      buffer_size = 4 * limits::kMinAudioBufferSize;
+  }
+  return buffer_size;
+}
+
+}  // namespace media
diff --git a/media/base/mac/audio_latency_mac.h b/media/base/mac/audio_latency_mac.h
new file mode 100644
index 0000000..e6ad4ef7
--- /dev/null
+++ b/media/base/mac/audio_latency_mac.h
@@ -0,0 +1,18 @@
+// 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 MEDIA_BASE_MAC_AUDIO_LATENCY_MAC_H_
+#define MEDIA_BASE_MAC_AUDIO_LATENCY_MAC_H_
+
+#include "base/macros.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+MEDIA_EXPORT int GetMinAudioBufferSizeMacOS(int min_buffer_size,
+                                            int sample_rate);
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_MAC_AUDIO_LATENCY_MAC_H_
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index a218682..8afb950 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -82,24 +82,22 @@
 // resources.
 enum { kMaxInFlightDecodes = 4 };
 
+struct GpuVideoDecoder::PendingDecoderBuffer {
+  PendingDecoderBuffer(std::unique_ptr<SHMBuffer> s,
+                       const scoped_refptr<DecoderBuffer>& b,
+                       const DecodeCB& done_cb)
+      : shm_buffer(std::move(s)), buffer(b), done_cb(done_cb) {}
+  std::unique_ptr<SHMBuffer> shm_buffer;
+  scoped_refptr<DecoderBuffer> buffer;
+  DecodeCB done_cb;
+};
+
 GpuVideoDecoder::SHMBuffer::SHMBuffer(std::unique_ptr<base::SharedMemory> m,
                                       size_t s)
     : shm(std::move(m)), size(s) {}
 
 GpuVideoDecoder::SHMBuffer::~SHMBuffer() {}
 
-GpuVideoDecoder::PendingDecoderBuffer::PendingDecoderBuffer(
-    SHMBuffer* s,
-    const scoped_refptr<DecoderBuffer>& b,
-    const DecodeCB& done_cb)
-    : shm_buffer(s), buffer(b), done_cb(done_cb) {
-}
-
-GpuVideoDecoder::PendingDecoderBuffer::PendingDecoderBuffer(
-    const PendingDecoderBuffer& other) = default;
-
-GpuVideoDecoder::PendingDecoderBuffer::~PendingDecoderBuffer() {}
-
 GpuVideoDecoder::BufferData::BufferData(int32_t bbid,
                                         base::TimeDelta ts,
                                         const gfx::Rect& vr,
@@ -461,9 +459,9 @@
   next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
   DCHECK(
       !base::ContainsKey(bitstream_buffers_in_decoder_, bitstream_buffer.id()));
-  bitstream_buffers_in_decoder_.insert(std::make_pair(
+  bitstream_buffers_in_decoder_.emplace(
       bitstream_buffer.id(),
-      PendingDecoderBuffer(shm_buffer.release(), buffer, decode_cb)));
+      PendingDecoderBuffer(std::move(shm_buffer), buffer, decode_cb));
   DCHECK_LE(static_cast<int>(bitstream_buffers_in_decoder_.size()),
             kMaxInFlightDecodes);
   RecordBufferData(bitstream_buffer, *buffer.get());
@@ -763,14 +761,14 @@
       return NULL;
     return base::MakeUnique<SHMBuffer>(std::move(shm), size_to_allocate);
   }
-  std::unique_ptr<SHMBuffer> ret(available_shm_segments_.back());
+  std::unique_ptr<SHMBuffer> ret(std::move(available_shm_segments_.back()));
   available_shm_segments_.pop_back();
   return ret;
 }
 
 void GpuVideoDecoder::PutSHM(std::unique_ptr<SHMBuffer> shm_buffer) {
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
-  available_shm_segments_.push_back(shm_buffer.release());
+  available_shm_segments_.push_back(std::move(shm_buffer));
 }
 
 void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32_t id) {
@@ -785,7 +783,7 @@
     return;
   }
 
-  PutSHM(base::WrapUnique(it->second.shm_buffer));
+  PutSHM(std::move(it->second.shm_buffer));
   it->second.done_cb.Run(state_ == kError ? DecodeStatus::DECODE_ERROR
                                           : DecodeStatus::OK);
   bitstream_buffers_in_decoder_.erase(it);
@@ -805,15 +803,9 @@
     base::ResetAndReturn(&request_overlay_info_cb_)
         .Run(false, ProvideOverlayInfoCB());
 
-  for (size_t i = 0; i < available_shm_segments_.size(); ++i) {
-    delete available_shm_segments_[i];
-  }
-  available_shm_segments_.clear();
-
   for (std::map<int32_t, PendingDecoderBuffer>::iterator it =
            bitstream_buffers_in_decoder_.begin();
        it != bitstream_buffers_in_decoder_.end(); ++it) {
-    delete it->second.shm_buffer;
     it->second.done_cb.Run(DecodeStatus::ABORTED);
   }
   bitstream_buffers_in_decoder_.clear();
diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h
index 634a46a..4463a0e 100644
--- a/media/filters/gpu_video_decoder.h
+++ b/media/filters/gpu_video_decoder.h
@@ -99,16 +99,7 @@
   };
 
   // A SHMBuffer and the DecoderBuffer its data came from.
-  struct PendingDecoderBuffer {
-    PendingDecoderBuffer(SHMBuffer* s,
-                        const scoped_refptr<DecoderBuffer>& b,
-                        const DecodeCB& done_cb);
-    PendingDecoderBuffer(const PendingDecoderBuffer& other);
-    ~PendingDecoderBuffer();
-    SHMBuffer* shm_buffer;
-    scoped_refptr<DecoderBuffer> buffer;
-    DecodeCB done_cb;
-  };
+  struct PendingDecoderBuffer;
 
   typedef std::map<int32_t, PictureBuffer> PictureBufferMap;
 
@@ -196,7 +187,7 @@
   // Shared-memory buffer pool.  Since allocating SHM segments requires a
   // round-trip to the browser process, we keep allocation out of the
   // steady-state of the decoder.
-  std::vector<SHMBuffer*> available_shm_segments_;
+  std::vector<std::unique_ptr<SHMBuffer>> available_shm_segments_;
 
   // Placeholder sync token that was created and validated after the most
   // recent picture buffers were created.
diff --git a/services/test/user_id/BUILD.gn b/services/test/user_id/BUILD.gn
new file mode 100644
index 0000000..b8853ec
--- /dev/null
+++ b/services/test/user_id/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//services/catalog/public/tools/catalog.gni")
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+
+source_set("lib") {
+  sources = [
+    "user_id_service.cc",
+    "user_id_service.h",
+  ]
+
+  deps = [
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/interfaces",
+    "//services/test/user_id/public/interfaces",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "user_id"
+  source = "manifest.json"
+}
diff --git a/services/test/user_id/README.md b/services/test/user_id/README.md
new file mode 100644
index 0000000..370b6bfb
--- /dev/null
+++ b/services/test/user_id/README.md
@@ -0,0 +1 @@
+A simple service that can be used for testing per-user services.
diff --git a/services/test/user_id/manifest.json b/services/test/user_id/manifest.json
new file mode 100644
index 0000000..a8708dd
--- /dev/null
+++ b/services/test/user_id/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name": "user_id",
+  "display_name": "User ID Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "user_id" : [ "user_id::mojom::UserId" ]
+      }
+    }
+  }
+}
diff --git a/services/test/user_id/public/interfaces/BUILD.gn b/services/test/user_id/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..a87f2fc
--- /dev/null
+++ b/services/test/user_id/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "user_id.mojom",
+  ]
+}
diff --git a/services/test/user_id/public/interfaces/OWNERS b/services/test/user_id/public/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/services/test/user_id/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/test/user_id/public/interfaces/user_id.mojom b/services/test/user_id/public/interfaces/user_id.mojom
new file mode 100644
index 0000000..a22579f
--- /dev/null
+++ b/services/test/user_id/public/interfaces/user_id.mojom
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module user_id.mojom;
+
+// Provides access to the user ID of the service in which this interface is
+// running.
+interface UserId {
+  // Returns the user ID of the service in which this interface is running.
+  GetUserId() => (string user_id);
+};
diff --git a/services/test/user_id/user_id_service.cc b/services/test/user_id/user_id_service.cc
new file mode 100644
index 0000000..34a12a8
--- /dev/null
+++ b/services/test/user_id/user_id_service.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/test/user_id/user_id_service.h"
+
+#include "services/service_manager/public/cpp/service_context.h"
+
+namespace user_id {
+
+std::unique_ptr<service_manager::Service> CreateUserIdService() {
+  return base::MakeUnique<UserIdService>();
+}
+
+UserIdService::UserIdService() {
+  registry_.AddInterface<mojom::UserId>(
+      base::Bind(&UserIdService::BindUserIdRequest, base::Unretained(this)));
+}
+
+UserIdService::~UserIdService() {}
+
+void UserIdService::OnStart() {}
+
+void UserIdService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(source_info, interface_name,
+                          std::move(interface_pipe));
+}
+
+void UserIdService::BindUserIdRequest(
+    const service_manager::BindSourceInfo& source_info,
+    mojom::UserIdRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void UserIdService::GetUserId(GetUserIdCallback callback) {
+  std::move(callback).Run(context()->identity().user_id());
+}
+
+}  // namespace user_id
diff --git a/services/test/user_id/user_id_service.h b/services/test/user_id/user_id_service.h
new file mode 100644
index 0000000..b8174a4
--- /dev/null
+++ b/services/test/user_id/user_id_service.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_USER_ID_USER_ID_SERVICE_H_
+#define SERVICES_USER_ID_USER_ID_SERVICE_H_
+
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/test/user_id/public/interfaces/user_id.mojom.h"
+
+namespace user_id {
+
+std::unique_ptr<service_manager::Service> CreateUserIdService();
+
+class UserIdService : public service_manager::Service, public mojom::UserId {
+ public:
+  UserIdService();
+  ~UserIdService() override;
+
+ private:
+  // service_manager::Service:
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  // mojom::UserId:
+  void GetUserId(GetUserIdCallback callback) override;
+
+  void BindUserIdRequest(const service_manager::BindSourceInfo& source_info,
+                         mojom::UserIdRequest request);
+
+  service_manager::BinderRegistry registry_;
+  mojo::BindingSet<mojom::UserId> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserIdService);
+};
+
+}  // namespace user_id
+
+#endif  // SERVICES_USER_ID_USER_ID_SERVICE_H_
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index fcdd5e1..9b17571 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -9,9 +9,6 @@
 # ask hajimehoshi@.                                                       #
 ###########################################################################
 
-# FIXME: The below tests fails when the leak detector is disabled.
-crbug.com/366029 compositing/fixed-body-background-positioned.html [ Failure Pass ]
-
 # FIXME: The below tests crashes when the leak detector is run on debug builds
 crbug.com/366043 [ Debug ] fast/history/history-traversal-is-asynchronous.html [ Crash ]
 
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index cc25550..e9406fde 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -252,7 +252,6 @@
 # WPT subdirectories without owners.
 external/wpt/accelerometer [ WontFix ]
 external/wpt/assumptions [ WontFix ]
-external/wpt/clear-site-data [ WontFix ]
 external/wpt/css/css-transforms-2 [ WontFix ]
 external/wpt/css-values [ WontFix ]
 external/wpt/gyroscope [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index 3534e37..edbe374 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -42,6 +42,8 @@
 # external/wpt/battery-status [ Pass ]
 ## Owners: ortuno@chromium.org,scheib@chromium.org
 # external/wpt/bluetooth [ Pass ]
+## Owners: msramek@chromium.org,mkwst@chromium.org
+# external/wpt/clear-site-data [ Pass ]
 ## Owners: none; No tests in the directory.
 # external/wpt/common [ Pass ]
 ## Owners: foolip@chromium.org
diff --git a/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object-expected.txt
new file mode 100644
index 0000000..52167af6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Test [[DefineOwnProperty]] with no indexed property setter support. assert_equals: (assert_prop_desc_equals: property 'writable') expected false but got true
+FAIL Test [[DefineOwnProperty]] with indexed property setter support. assert_equals: (assert_prop_desc_equals: property 'enumerable') expected true but got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object.html b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object.html
new file mode 100644
index 0000000..0c5326f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/ecmascript-binding/legacy-platform-object.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Legacy platform objects</title>
+<link rel="help" href="https://heycam.github.io/webidl/#es-legacy-platform-objects">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function assert_prop_desc_equals(object, property_key, expected) {
+  let actual = Object.getOwnPropertyDescriptor(object, property_key);
+  if (expected === undefined) {
+    assert_equals(
+        actual, undefined,
+        "(assert_prop_desc_equals: no property descriptor expected)");
+    return;
+  }
+  for (p in actual) {
+    assert_true(
+        expected.hasOwnProperty(p),
+        "(assert_prop_desc_equals: property '" + p + "' is not expected)");
+    assert_equals(
+        actual[p], expected[p],
+        "(assert_prop_desc_equals: property '" + p + "')");
+  }
+  for (p in expected) {
+    assert_true(
+        actual.hasOwnProperty(p),
+        "(assert_prop_desc_equals: expected property '" + p + "' missing)");
+  }
+}
+
+// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+// 3.9.3. [[DefineOwnProperty]]
+
+test(function() {
+  let span = document.createElement("span");
+  span.className = "foo";
+  // DOMTokenList supports an indexed property getter but not a setter.
+  let domTokenList = span.classList;
+  // Confirm the test settings.
+  assert_equals(domTokenList.length, 1);
+  assert_prop_desc_equals(domTokenList, "0",
+                          {value: "foo", writable: false, enumerable: true,
+                           configurable: true});
+  assert_prop_desc_equals(domTokenList, "1", undefined);
+  // Actual test
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "0", {value: true, writable: true}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "1", {value: true, writable: true}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "0", {get: () => {}}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "0", {set: () => {}}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "1", {get: () => {}}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(domTokenList, "1", {set: () => {}}));
+  assert_equals(domTokenList[0], "foo");
+  assert_equals(domTokenList[1], undefined);
+  domTokenList[0] = "bar";
+  domTokenList[1] = "bar";
+  assert_equals(domTokenList[0], "foo");
+  assert_equals(domTokenList[1], undefined);
+  assert_throws(new TypeError(), () => {
+    "use strict";
+    domTokenList[0] = "bar";
+  });
+  assert_throws(new TypeError(), () => {
+    "use strict";
+    domTokenList[1] = "bar";
+  });
+  // Nothing must change after all.
+  assert_equals(domTokenList.length, 1);
+  assert_prop_desc_equals(domTokenList, "0",
+                          {value: "foo", writable: false, enumerable: true,
+                           configurable: true});
+  assert_prop_desc_equals(domTokenList, "1", undefined);
+}, "Test [[DefineOwnProperty]] with no indexed property setter support.");
+
+test(function() {
+  // HTMLSelectElement supports an indexed property setter.
+  let select = document.createElement("select");
+  let option0 = document.createElement("option");
+  let option1 = document.createElement("option");
+  // Confirm the test settings.
+  assert_equals(select.length, 0);
+  assert_prop_desc_equals(select, "0", undefined);
+  // Try to define an accessor property with non supported property index.
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(select, "0", {get: () => {}}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(select, "0", {set: () => {}}));
+  assert_prop_desc_equals(select, "0", undefined);
+  // writable, enumerable, configurable will be ignored.
+  Object.defineProperty(select, "0", {value: option0, writable: false,
+                                      enumerable: false, configurable: false});
+  assert_prop_desc_equals(select, "0",
+                          {value: option0, writable: true, enumerable: true,
+                           configurable: true});
+  select[1] = option1;
+  assert_prop_desc_equals(select, "1",
+                          {value: option1, writable: true, enumerable: true,
+                           configurable: true});
+  // Try to define an accessor property with a supported property index.
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(select, "0", {get: () => {}}));
+  assert_throws(new TypeError(), () =>
+      Object.defineProperty(select, "0", {set: () => {}}));
+  assert_prop_desc_equals(select, "0",
+                          {value: option0, writable: true, enumerable: true,
+                           configurable: true});
+}, "Test [[DefineOwnProperty]] with indexed property setter support.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation-insecure.html b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation-insecure.html
new file mode 100644
index 0000000..c156aa10
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation-insecure.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/test_utils.js"></script>
+  </head>
+
+  <body>
+    <script>
+      /**
+       * @param Array.<Array.<Datatype>> combination A combination of datatypes.
+       * @param Dict.<string, boolean> report A map between a datatype name and
+       *     whether it is empty.
+       * @return boolean Whether all datatypes are still nonempty.
+       */
+      function verifyDatatypes(report) {
+        TestUtils.DATATYPES.forEach(function(datatype) {
+          assert_false(
+              report[datatype.name],
+              datatype.name + " should NOT have been cleared.");
+        });
+      }
+
+      TestUtils.COMBINATIONS.forEach(function(combination) {
+        var test_name =
+            "Do not clear datatypes on insecure navigation (header: " +
+            combination.map(function(e) { return e.name; }).join(", ") +
+            ")";
+
+        promise_test(function(test) {
+          return new Promise(function(resolve_test, reject_test) {
+            TestUtils.populateDatatypes()
+                .then(function() {
+                  // Navigate to a resource with a Clear-Site-Data header in
+                  // an iframe, then verify that no data have been deleted.
+                  return new Promise(function(resolve, reject) {
+                    window.addEventListener("message", resolve);
+                    var iframe = document.createElement("iframe");
+                    iframe.src = TestUtils.getClearSiteDataUrl(combination);
+                    document.body.appendChild(iframe);
+                  }).then(function(messageEvent) {
+                    verifyDatatypes(messageEvent.data);
+                    resolve_test();
+                  });
+                });
+          });
+        }, test_name);
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.html b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.html
deleted file mode 100644
index cd0f83e1..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <script src="/resources/testharness.js"></script>
-    <script src="/resources/testharnessreport.js"></script>
-    <script src="support/test_utils.js"></script>
-  </head>
-
-  <body>
-    <script>
-      /** Ensures that all datatypes are nonempty. */
-      function populateDatatypes() {
-        return Promise.all(TestUtils.DATATYPES.map(function(datatype) {
-          return datatype.add().then(datatype.isEmpty().then(function(isEmpty) {
-            assert_false(
-                isEmpty,
-                datatype.name + " has to be nonempty before the test starts.");
-          }));
-        }));
-      }
-
-      /**
-       * @param Array.<Array.<Datatype>> combination A combination of datatypes.
-       * @param Dict.<string, boolean> report A map between a datatype name and
-       *     whether it is empty.
-       * @return boolean Whether all datatypes are empty if and only if they are
-       *     included in the |combination|.
-       */
-      function verifyDatatypes(combination, report) {
-        TestUtils.DATATYPES.forEach(function(datatype) {
-          if (combination.indexOf(datatype) != -1) {
-            assert_true(
-                report[datatype.name],
-                datatype.name + " should have been cleared.");
-          } else {
-            assert_false(
-                report[datatype.name],
-                datatype.name + " should NOT have been cleared.");
-          }
-        })
-      }
-
-      TestUtils.COMBINATIONS.forEach(function(combination) {
-        var test_name =
-            "Clear datatypes on navigation: " +
-            combination.map(function(e) { return e.name; }).join(", ");
-
-        promise_test(function(test) {
-          return populateDatatypes()
-              .then(function() {
-                // Navigate to a resource with a Clear-Site-Data header in an
-                // iframe, then verify that the correct types have been deleted.
-                return new Promise(function(resolve, reject) {
-                  window.addEventListener("message", resolve);
-                  var iframe = document.createElement("iframe");
-                  iframe.src = TestUtils.getClearSiteDataUrl(combination);
-                  document.body.appendChild(iframe);
-                }).then(function(messageEvent) {
-                  verifyDatatypes(combination, messageEvent.data);
-                });
-              });
-        }, test_name);
-      });
-    </script>
-  </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.https.html b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.https.html
new file mode 100644
index 0000000..a8f83903e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/navigation.https.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/test_utils.js"></script>
+  </head>
+
+  <body>
+    <script>
+      /**
+       * @param Array.<Array.<Datatype>> combination A combination of datatypes.
+       * @param Dict.<string, boolean> report A map between a datatype name and
+       *     whether it is empty.
+       * @return boolean Whether all datatypes are empty if and only if they are
+       *     included in the |combination|.
+       */
+      function verifyDatatypes(combination, report) {
+        TestUtils.DATATYPES.forEach(function(datatype) {
+          if (combination.indexOf(datatype) != -1) {
+            assert_true(
+                report[datatype.name],
+                datatype.name + " should have been cleared.");
+          } else {
+            assert_false(
+                report[datatype.name],
+                datatype.name + " should NOT have been cleared.");
+          }
+        });
+      }
+
+      TestUtils.COMBINATIONS.forEach(function(combination) {
+        var test_name =
+            "Clear datatypes on navigation: " +
+            combination.map(function(e) { return e.name; }).join(", ");
+
+        promise_test(function(test) {
+          return new Promise(function(resolve_test, reject_test) {
+            TestUtils.populateDatatypes()
+                .then(function() {
+                  // Navigate to a resource with a Clear-Site-Data header in
+                  // an iframe, then verify that the correct types have been
+                  // deleted.
+                  return new Promise(function(resolve, reject) {
+                    window.addEventListener("message", resolve);
+                    var iframe = document.createElement("iframe");
+                    iframe.src = TestUtils.getClearSiteDataUrl(combination);
+                    document.body.appendChild(iframe);
+                  }).then(function(messageEvent) {
+                    verifyDatatypes(combination, messageEvent.data);
+                    resolve_test();
+                  });
+                });
+          });
+        }, test_name);
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/echo-clear-site-data.py b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/echo-clear-site-data.py
index e8ec8d55..ab85002 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/echo-clear-site-data.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/echo-clear-site-data.py
@@ -34,7 +34,7 @@
 # embedder whether the data deletion succeeded.
 def main(request, response):
     types = [key for key in request.GET.keys()]
-    header = json.dumps({ "types": types })
+    header = ",".join("\"" + type + "\"" for type in types)
     return ([("Clear-Site-Data", header),
              ("Content-Type", "text/html")],
             RESPONSE)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/test_utils.js b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/test_utils.js
index 6aff373..35b0a01 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/test_utils.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/clear-site-data/support/test_utils.js
@@ -75,6 +75,26 @@
   })();
 
   /**
+   * Ensures that all datatypes are nonempty. Should be called in the test
+   * setup phase.
+   */
+  TestUtils.populateDatatypes = function() {
+    return Promise.all(TestUtils.DATATYPES.map(function(datatype) {
+      return new Promise(function(resolve, reject) {
+        datatype.add().then(function() {
+          datatype.isEmpty().then(function(isEmpty) {
+            assert_false(
+                isEmpty,
+                datatype.name +
+                    " has to be nonempty before the test starts.");
+            resolve();
+          });
+        });
+      });
+    }));
+  };
+
+  /**
    * Get the support server URL that returns a Clear-Site-Data header
    * to clear |datatypes|.
    * @param{Array.<Datatype>} datatypes The list of datatypes to be deleted.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/collections/HTMLCollection-supported-property-indices-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/collections/HTMLCollection-supported-property-indices-expected.txt
index e9f5af1..6d9d338 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/collections/HTMLCollection-supported-property-indices-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/collections/HTMLCollection-supported-property-indices-expected.txt
@@ -4,7 +4,7 @@
 PASS Handling of property names that look like integers around 2^31 
 PASS Handling of property names that look like integers around 2^32 
 FAIL Trying to set an expando that would shadow an already-existing indexed property assert_true: expected true got false
-FAIL Trying to set an expando with an indexed property name past the end of the list assert_equals: expected (undefined) undefined but got (number) 5
+PASS Trying to set an expando with an indexed property name past the end of the list 
 FAIL Trying to delete an indexed property name should never work assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Document-getElementsByTagName-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Document-getElementsByTagName-expected.txt
index 59d64579..9b84c84 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Document-getElementsByTagName-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Document-getElementsByTagName-expected.txt
@@ -1,11 +1,8 @@
 This is a testharness.js-based test.
 PASS Interfaces 
 PASS Caching is allowed 
-FAIL Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode) assert_equals: expected (undefined) undefined but got (string) "foopy"
-FAIL Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode) assert_throws: function "function () {
-      "use strict";
-      l[5] = "foopy"
-    }" did not throw
+PASS Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode) 
+PASS Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode) 
 PASS Should be able to set expando shadowing a proto prop (item) 
 PASS Should be able to set expando shadowing a proto prop (namedItem) 
 FAIL hasOwnProperty, getOwnPropertyDescriptor, getOwnPropertyNames assert_true: desc.enumerable expected true got false
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-getElementsByTagName-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-getElementsByTagName-expected.txt
index efd83ed..8310187 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-getElementsByTagName-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-getElementsByTagName-expected.txt
@@ -1,11 +1,8 @@
 This is a testharness.js-based test.
 PASS Interfaces 
 PASS Caching is allowed 
-FAIL Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode) assert_equals: expected (undefined) undefined but got (string) "foopy"
-FAIL Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode) assert_throws: function "function () {
-      "use strict";
-      l[5] = "foopy"
-    }" did not throw
+PASS Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode) 
+PASS Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode) 
 PASS Should be able to set expando shadowing a proto prop (item) 
 PASS Should be able to set expando shadowing a proto prop (namedItem) 
 FAIL hasOwnProperty, getOwnPropertyDescriptor, getOwnPropertyNames assert_true: desc.enumerable expected true got false
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-expected.txt
index f053637..8e34f3e8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
 PASS Indexed properties of the window object (non-strict mode) 
-FAIL Indexed properties of the window object (non-strict mode) 1 assert_throws: function "() => Object.defineProperty(window, 0, { value: "bar" })" did not throw
-FAIL Indexed properties of the window object (non-strict mode) 2 assert_throws: function "() => Object.defineProperty(window, 1, { value: "bar" })" did not throw
+FAIL Indexed properties of the window object (non-strict mode) 1 assert_equals: expected false but got true
+PASS Indexed properties of the window object (non-strict mode) 2 
 PASS Indexed properties of the window object (non-strict mode) 3 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-strict-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-strict-expected.txt
index c0da5b2f..6c32654 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-strict-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/the-window-object/window-indexed-properties-strict-expected.txt
@@ -1,11 +1,7 @@
 This is a testharness.js-based test.
 PASS Indexed properties of the window object (strict mode) 
-FAIL Indexed properties of the window object (strict mode) 1 assert_throws: function "function () {
-    window[0] = "foo";
-  }" did not throw
-FAIL Indexed properties of the window object (strict mode) 2 assert_throws: function "function () {
-    window[1] = "foo";
-  }" did not throw
+FAIL Indexed properties of the window object (strict mode) 1 assert_throws: function "() => delete window[0]" did not throw
+PASS Indexed properties of the window object (strict mode) 2 
 PASS Indexed properties of the window object (strict mode) 3 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter-expected.txt
deleted file mode 100644
index 1a9b070..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS TextTrackCueList getter 
-FAIL TextTrackCueList getter, no indexed set/create assert_equals: expected (undefined) undefined but got (string) "foo"
-FAIL TextTrackCueList getter, no indexed set/create (strict) assert_equals: expected (undefined) undefined but got (string) "foo"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter-expected.txt
deleted file mode 100644
index a04bfe2..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS TextTrackList getter 
-PASS TextTrackList getter, no indexed set/create 
-FAIL TextTrackList getter, no indexed set/create (strict) assert_throws: function "function (){ video.textTracks[0] = 'foo'; }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackCueList/getter-expected.txt b/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackCueList/getter-expected.txt
deleted file mode 100644
index 1a9b070..0000000
--- a/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackCueList/getter-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS TextTrackCueList getter 
-FAIL TextTrackCueList getter, no indexed set/create assert_equals: expected (undefined) undefined but got (string) "foo"
-FAIL TextTrackCueList getter, no indexed set/create (strict) assert_equals: expected (undefined) undefined but got (string) "foo"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackList/getter-expected.txt b/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackList/getter-expected.txt
deleted file mode 100644
index a04bfe2..0000000
--- a/third_party/WebKit/LayoutTests/media/track/opera/interfaces/TextTrackList/getter-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS TextTrackList getter 
-PASS TextTrackList getter, no indexed set/create 
-FAIL TextTrackList getter, no indexed set/create (strict) assert_throws: function "function (){ video.textTracks[0] = 'foo'; }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
index ce5780e5..16f28f3 100644
--- a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
@@ -111,7 +111,7 @@
 
 {##############################################################################}
 {% block indexed_property_setter_callback %}
-{% if indexed_property_setter or named_property_setter %}
+{% if indexed_property_getter or named_property_setter %}
 {% set setter = indexed_property_setter or named_property_setter %}
 void {{v8_class_or_partial}}::indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<v8::Value>& info) {
   {% if setter.is_ce_reactions %}
@@ -126,7 +126,7 @@
   {{cpp_class}}V8Internal::indexedPropertySetter(index, v8Value, info);
   {% endif %}
 
-  {% else %}{# otherwise, named property #}
+  {% elif named_property_setter %}
 
   const AtomicString& propertyName = AtomicString::Number(index);
 
@@ -136,7 +136,19 @@
   {{cpp_class}}V8Internal::namedPropertySetter(propertyName, v8Value, info);
   {% endif %}
 
-  {% endif %}{# indexed_property_setter #}
+  {% else %}{# neither of indexed_property_setter nor named_property_setter #}
+
+  // No indexed property setter defined.  Do not fall back to the default
+  // setter.
+  V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+  if (info.ShouldThrowOnError()) {
+    ExceptionState exceptionState(info.GetIsolate(),
+                                  ExceptionState::kIndexedSetterContext,
+                                  "{{interface_name}}");
+    exceptionState.ThrowTypeError("Index property setter is not supported.");
+  }
+
+  {% endif %}{# indexed/named_property_setter #}
 }
 
 {% endif %}
@@ -212,6 +224,53 @@
 
 
 {##############################################################################}
+{% block indexed_property_definer_callback %}
+{% if indexed_property_getter %}
+void {{v8_class_or_partial}}::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+{% if indexed_property_setter %}
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "{{interface_name}}");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+{% else %}{# indexed_property_setter #}
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.2. If O does not implement an interface with an indexed property
+  //   setter, then return false.
+  //
+  // https://html.spec.whatwg.org/C/window-object.html#windowproxy-defineownproperty
+  // 7.4.6 [[DefineOwnProperty]] (P, Desc)
+  // step 2.1. If P is an array index property name, return false.
+  V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+  if (info.ShouldThrowOnError()) {
+    ExceptionState exceptionState(info.GetIsolate(),
+                                  ExceptionState::kIndexedSetterContext,
+                                  "{{interface_name}}");
+    exceptionState.ThrowTypeError("Index property setter is not supported.");
+  }
+{% endif %}{# indexed_property_setter #}
+}
+
+{% endif %}{# indexed_property_getter #}
+{% endblock %}
+
+
+{##############################################################################}
 {% block named_property_getter %}
 {% if named_property_getter and not named_property_getter.is_custom %}
 {% set getter = named_property_getter %}
@@ -707,17 +766,28 @@
        '%s::indexedPropertyGetterCallback' % v8_class_or_partial %}
 {% set indexed_property_setter_callback =
        '%s::indexedPropertySetterCallback' % v8_class_or_partial
-       if indexed_property_setter or named_property_setter else 'nullptr' %}
-{% set indexed_property_query_callback = 'nullptr' %}{# Unused #}
+       if indexed_property_getter or named_property_setter else 'nullptr' %}
+{% set indexed_property_descriptor_callback = 'nullptr' %}
 {% set indexed_property_deleter_callback =
        '%s::indexedPropertyDeleterCallback' % v8_class_or_partial
        if indexed_property_deleter or named_property_deleter else 'nullptr' %}
 {% set indexed_property_enumerator_callback =
        'IndexedPropertyEnumerator<%s>' % cpp_class
        if indexed_property_getter.is_enumerable else 'nullptr' %}
+{% set indexed_property_definer_callback =
+       '%s::indexedPropertyDefinerCallback' % v8_class_or_partial
+       if indexed_property_getter else 'nullptr' %}
 {% set property_handler_flags =
        'v8::PropertyHandlerFlags::kNone' %}
-v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig({{indexed_property_getter_callback}}, {{indexed_property_setter_callback}}, {{indexed_property_query_callback}}, {{indexed_property_deleter_callback}}, {{indexed_property_enumerator_callback}}, v8::Local<v8::Value>(), {{property_handler_flags}});
+v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+    {{indexed_property_getter_callback}},
+    {{indexed_property_setter_callback}},
+    {{indexed_property_descriptor_callback}},
+    {{indexed_property_deleter_callback}},
+    {{indexed_property_enumerator_callback}},
+    {{indexed_property_definer_callback}},
+    v8::Local<v8::Value>(),
+    {{property_handler_flags}});
 {{target}}->SetHandler(indexedPropertyHandlerConfig);
 {%- endmacro %}
 
diff --git a/third_party/WebKit/Source/bindings/templates/interface.h.tmpl b/third_party/WebKit/Source/bindings/templates/interface.h.tmpl
index b294016..d2b9735 100644
--- a/third_party/WebKit/Source/bindings/templates/interface.h.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface.h.tmpl
@@ -245,12 +245,15 @@
   {% if indexed_property_getter or named_property_getter %}
   {{exported}}static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   {% endif %}
-  {% if indexed_property_setter or named_property_setter %}
+  {% if indexed_property_getter or named_property_setter %}
   {{exported}}static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   {% endif %}
   {% if indexed_property_deleter or named_property_deleter %}
   {{exported}}static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
   {% endif %}
+  {% if indexed_property_getter %}
+  {{exported}}static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
+  {% endif %}
 
   {% if needs_runtime_enabled_installer %}
   {{exported if has_partial_interface else ''}}static void InstallRuntimeEnabledFeatures(
diff --git a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
index 37623a3..ea50c0a 100644
--- a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
@@ -244,6 +244,7 @@
 {% block indexed_property_getter_callback %}{% endblock %}
 {% block indexed_property_setter_callback %}{% endblock %}
 {% block indexed_property_deleter_callback %}{% endblock %}
+{% block indexed_property_definer_callback %}{% endblock %}
 
 {% if has_access_check_callbacks and not is_partial %}
 bool {{v8_class_or_partial}}::securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> data) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
index 9be6fb7a..a77e2aa 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
@@ -177,6 +177,28 @@
   V8TestIntegerIndexed::indexedPropertyDeleterCustom(index, info);
 }
 
+void V8TestIntegerIndexed::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestIntegerIndexed");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 static const V8DOMConfiguration::AccessorConfiguration V8TestIntegerIndexedAccessors[] = {
     { "length", V8TestIntegerIndexed::lengthAttributeGetterCallback, V8TestIntegerIndexed::lengthAttributeSetterCallback, nullptr, nullptr, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kAllWorlds },
 };
@@ -208,7 +230,15 @@
       signature, V8TestIntegerIndexedMethods, WTF_ARRAY_LENGTH(V8TestIntegerIndexedMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestIntegerIndexed::indexedPropertyGetterCallback, V8TestIntegerIndexed::indexedPropertySetterCallback, nullptr, V8TestIntegerIndexed::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestIntegerIndexed>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestIntegerIndexed::indexedPropertyGetterCallback,
+      V8TestIntegerIndexed::indexedPropertySetterCallback,
+      nullptr,
+      V8TestIntegerIndexed::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestIntegerIndexed>,
+      V8TestIntegerIndexed::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestIntegerIndexed::namedPropertyGetterCallback, V8TestIntegerIndexed::namedPropertySetterCallback, V8TestIntegerIndexed::namedPropertyQueryCallback, V8TestIntegerIndexed::namedPropertyDeleterCallback, V8TestIntegerIndexed::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.h
index 01f15d6..77a16fd1 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.h
@@ -67,6 +67,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
index 7cbb68b5..809d9c6c 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
@@ -177,6 +177,28 @@
   V8TestIntegerIndexedGlobal::indexedPropertyDeleterCustom(index, info);
 }
 
+void V8TestIntegerIndexedGlobal::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestIntegerIndexedGlobal");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 static const V8DOMConfiguration::AccessorConfiguration V8TestIntegerIndexedGlobalAccessors[] = {
     { "length", V8TestIntegerIndexedGlobal::lengthAttributeGetterCallback, V8TestIntegerIndexedGlobal::lengthAttributeSetterCallback, nullptr, nullptr, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnInstance, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kAllWorlds },
 };
@@ -214,7 +236,15 @@
       signature, V8TestIntegerIndexedGlobalMethods, WTF_ARRAY_LENGTH(V8TestIntegerIndexedGlobalMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestIntegerIndexedGlobal::indexedPropertyGetterCallback, V8TestIntegerIndexedGlobal::indexedPropertySetterCallback, nullptr, V8TestIntegerIndexedGlobal::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestIntegerIndexedGlobal>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestIntegerIndexedGlobal::indexedPropertyGetterCallback,
+      V8TestIntegerIndexedGlobal::indexedPropertySetterCallback,
+      nullptr,
+      V8TestIntegerIndexedGlobal::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestIntegerIndexedGlobal>,
+      V8TestIntegerIndexedGlobal::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
 
   // Array iterator (@@iterator)
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.h
index a2313828..daac288 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.h
@@ -68,6 +68,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
index 6f75b849..7c97aa8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
@@ -177,6 +177,28 @@
   V8TestIntegerIndexedPrimaryGlobal::indexedPropertyDeleterCustom(index, info);
 }
 
+void V8TestIntegerIndexedPrimaryGlobal::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestIntegerIndexedPrimaryGlobal");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 static const V8DOMConfiguration::AccessorConfiguration V8TestIntegerIndexedPrimaryGlobalAccessors[] = {
     { "length", V8TestIntegerIndexedPrimaryGlobal::lengthAttributeGetterCallback, V8TestIntegerIndexedPrimaryGlobal::lengthAttributeSetterCallback, nullptr, nullptr, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnInstance, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kAllWorlds },
 };
@@ -214,7 +236,15 @@
       signature, V8TestIntegerIndexedPrimaryGlobalMethods, WTF_ARRAY_LENGTH(V8TestIntegerIndexedPrimaryGlobalMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestIntegerIndexedPrimaryGlobal::indexedPropertyGetterCallback, V8TestIntegerIndexedPrimaryGlobal::indexedPropertySetterCallback, nullptr, V8TestIntegerIndexedPrimaryGlobal::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestIntegerIndexedPrimaryGlobal>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestIntegerIndexedPrimaryGlobal::indexedPropertyGetterCallback,
+      V8TestIntegerIndexedPrimaryGlobal::indexedPropertySetterCallback,
+      nullptr,
+      V8TestIntegerIndexedPrimaryGlobal::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestIntegerIndexedPrimaryGlobal>,
+      V8TestIntegerIndexedPrimaryGlobal::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
 
   // Array iterator (@@iterator)
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.h
index 502e16f..4d3faa4d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.h
@@ -68,6 +68,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
index 9ecd142..892677490 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
@@ -2898,6 +2898,28 @@
   TestInterfaceImplementationV8Internal::indexedPropertyDeleter(index, info);
 }
 
+void V8TestInterface::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestInterface");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
 #if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
@@ -3031,7 +3053,15 @@
       signature, V8TestInterfaceMethods, WTF_ARRAY_LENGTH(V8TestInterfaceMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestInterface::indexedPropertyGetterCallback, V8TestInterface::indexedPropertySetterCallback, nullptr, V8TestInterface::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestInterfaceImplementation>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestInterface::indexedPropertyGetterCallback,
+      V8TestInterface::indexedPropertySetterCallback,
+      nullptr,
+      V8TestInterface::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestInterfaceImplementation>,
+      V8TestInterface::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestInterface::namedPropertyGetterCallback, V8TestInterface::namedPropertySetterCallback, V8TestInterface::namedPropertyQueryCallback, V8TestInterface::namedPropertyDeleterCallback, V8TestInterface::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.h
index 31e5de10..b66c4036 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.h
@@ -223,6 +223,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   CORE_EXPORT static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
index 670b815..7613d4bd 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
@@ -587,6 +587,28 @@
   TestInterface2V8Internal::indexedPropertyDeleter(index, info);
 }
 
+void V8TestInterface2::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestInterface2");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 static const V8DOMConfiguration::AccessorConfiguration V8TestInterface2Accessors[] = {
     { "size", V8TestInterface2::sizeAttributeGetterCallback, nullptr, nullptr, nullptr, static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kAllWorlds },
 };
@@ -647,7 +669,15 @@
       signature, V8TestInterface2Methods, WTF_ARRAY_LENGTH(V8TestInterface2Methods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestInterface2::indexedPropertyGetterCallback, V8TestInterface2::indexedPropertySetterCallback, nullptr, V8TestInterface2::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestInterface2>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestInterface2::indexedPropertyGetterCallback,
+      V8TestInterface2::indexedPropertySetterCallback,
+      nullptr,
+      V8TestInterface2::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestInterface2>,
+      V8TestInterface2::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestInterface2::namedPropertyGetterCallback, V8TestInterface2::namedPropertySetterCallback, V8TestInterface2::namedPropertyQueryCallback, V8TestInterface2::namedPropertyDeleterCallback, V8TestInterface2::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.h
index 40384c7..4c6c8bc7 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.h
@@ -80,6 +80,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   CORE_EXPORT static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
index e3cd8f5..53453a3 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
@@ -174,6 +174,28 @@
   V8TestInterface3::indexedPropertyDeleterCustom(index, info);
 }
 
+void V8TestInterface3::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestInterface3");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 static const V8DOMConfiguration::AccessorConfiguration V8TestInterface3Accessors[] = {
     { "length", V8TestInterface3::lengthAttributeGetterCallback, nullptr, nullptr, nullptr, static_cast<v8::PropertyAttribute>(v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kAllWorlds },
 
@@ -208,7 +230,15 @@
       signature, V8TestInterface3Methods, WTF_ARRAY_LENGTH(V8TestInterface3Methods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestInterface3::indexedPropertyGetterCallback, V8TestInterface3::indexedPropertySetterCallback, nullptr, V8TestInterface3::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestInterface3>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestInterface3::indexedPropertyGetterCallback,
+      V8TestInterface3::indexedPropertySetterCallback,
+      nullptr,
+      V8TestInterface3::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestInterface3>,
+      V8TestInterface3::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestInterface3::namedPropertyGetterCallback, V8TestInterface3::namedPropertySetterCallback, V8TestInterface3::namedPropertyQueryCallback, V8TestInterface3::namedPropertyDeleterCallback, V8TestInterface3::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.h
index e84d2da..1acec8c8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.h
@@ -68,6 +68,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index e5cfffe..abca1805 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -12266,6 +12266,28 @@
   TestObjectV8Internal::indexedPropertyDeleter(index, info);
 }
 
+void V8TestObject::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestObject");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
 #if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
@@ -12874,7 +12896,15 @@
       signature, V8TestObjectMethods, WTF_ARRAY_LENGTH(V8TestObjectMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestObject::indexedPropertyGetterCallback, V8TestObject::indexedPropertySetterCallback, nullptr, V8TestObject::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestObject>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestObject::indexedPropertyGetterCallback,
+      V8TestObject::indexedPropertySetterCallback,
+      nullptr,
+      V8TestObject::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestObject>,
+      V8TestObject::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestObject::namedPropertyGetterCallback, V8TestObject::namedPropertySetterCallback, V8TestObject::namedPropertyQueryCallback, V8TestObject::namedPropertyDeleterCallback, V8TestObject::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
index 0b6ea49..9edbb10 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
@@ -641,6 +641,7 @@
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
index 99da49e..5dd7fff 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
@@ -206,7 +206,15 @@
       signature, V8TestSpecialOperationsMethods, WTF_ARRAY_LENGTH(V8TestSpecialOperationsMethods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestSpecialOperations::indexedPropertyGetterCallback, V8TestSpecialOperations::indexedPropertySetterCallback, nullptr, nullptr, nullptr, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestSpecialOperations::indexedPropertyGetterCallback,
+      V8TestSpecialOperations::indexedPropertySetterCallback,
+      nullptr,
+      nullptr,
+      nullptr,
+      nullptr,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestSpecialOperations::namedPropertyGetterCallback, V8TestSpecialOperations::namedPropertySetterCallback, V8TestSpecialOperations::namedPropertyQueryCallback, nullptr, V8TestSpecialOperations::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
index 567dea7d..7964949 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
@@ -101,6 +101,39 @@
   TestSpecialOperationsNotEnumerableV8Internal::indexedPropertyGetter(index, info);
 }
 
+void V8TestSpecialOperationsNotEnumerable::indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // No indexed property setter defined.  Do not fall back to the default
+  // setter.
+  V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+  if (info.ShouldThrowOnError()) {
+    ExceptionState exceptionState(info.GetIsolate(),
+                                  ExceptionState::kIndexedSetterContext,
+                                  "TestSpecialOperationsNotEnumerable");
+    exceptionState.ThrowTypeError("Index property setter is not supported.");
+  }
+}
+
+void V8TestSpecialOperationsNotEnumerable::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.2. If O does not implement an interface with an indexed property
+  //   setter, then return false.
+  //
+  // https://html.spec.whatwg.org/C/window-object.html#windowproxy-defineownproperty
+  // 7.4.6 [[DefineOwnProperty]] (P, Desc)
+  // step 2.1. If P is an array index property name, return false.
+  V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+  if (info.ShouldThrowOnError()) {
+    ExceptionState exceptionState(info.GetIsolate(),
+                                  ExceptionState::kIndexedSetterContext,
+                                  "TestSpecialOperationsNotEnumerable");
+    exceptionState.ThrowTypeError("Index property setter is not supported.");
+  }
+}
+
 static void installV8TestSpecialOperationsNotEnumerableTemplate(
     v8::Isolate* isolate,
     const DOMWrapperWorld& world,
@@ -118,7 +151,15 @@
   // Register IDL constants, attributes and operations.
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestSpecialOperationsNotEnumerable::indexedPropertyGetterCallback, nullptr, nullptr, nullptr, nullptr, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestSpecialOperationsNotEnumerable::indexedPropertyGetterCallback,
+      V8TestSpecialOperationsNotEnumerable::indexedPropertySetterCallback,
+      nullptr,
+      nullptr,
+      nullptr,
+      V8TestSpecialOperationsNotEnumerable::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestSpecialOperationsNotEnumerable::namedPropertyGetterCallback, nullptr, nullptr, nullptr, nullptr, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.h
index 2e58dfe..628a774 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.h
@@ -48,6 +48,8 @@
 
   CORE_EXPORT static void namedPropertyGetterCallback(v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>&);
   CORE_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
index 2e5a6e3b..271f472 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
@@ -809,6 +809,28 @@
   TestInterface5ImplementationV8Internal::indexedPropertyDeleter(index, info);
 }
 
+void V8TestInterface5::indexedPropertyDefinerCallback(
+    uint32_t index,
+    const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+  // 3.9.3. [[DefineOwnProperty]]
+  // step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+  //   return false.
+  if (desc.has_get() || desc.has_set()) {
+    V8SetReturnValue(info, v8::Null(info.GetIsolate()));
+    if (info.ShouldThrowOnError()) {
+      ExceptionState exceptionState(info.GetIsolate(),
+                                    ExceptionState::kIndexedSetterContext,
+                                    "TestInterface5");
+      exceptionState.ThrowTypeError("Accessor properties are not allowed.");
+    }
+    return;
+  }
+
+  // Return nothing and fall back to indexedPropertySetterCallback.
+}
+
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
 #if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
@@ -892,7 +914,15 @@
       signature, V8TestInterface5Methods, WTF_ARRAY_LENGTH(V8TestInterface5Methods));
 
   // Indexed properties
-  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(V8TestInterface5::indexedPropertyGetterCallback, V8TestInterface5::indexedPropertySetterCallback, nullptr, V8TestInterface5::indexedPropertyDeleterCallback, IndexedPropertyEnumerator<TestInterface5Implementation>, v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kNone);
+  v8::IndexedPropertyHandlerConfiguration indexedPropertyHandlerConfig(
+      V8TestInterface5::indexedPropertyGetterCallback,
+      V8TestInterface5::indexedPropertySetterCallback,
+      nullptr,
+      V8TestInterface5::indexedPropertyDeleterCallback,
+      IndexedPropertyEnumerator<TestInterface5Implementation>,
+      V8TestInterface5::indexedPropertyDefinerCallback,
+      v8::Local<v8::Value>(),
+      v8::PropertyHandlerFlags::kNone);
   instanceTemplate->SetHandler(indexedPropertyHandlerConfig);
   // Named properties
   v8::NamedPropertyHandlerConfiguration namedPropertyHandlerConfig(V8TestInterface5::namedPropertyGetterCallback, V8TestInterface5::namedPropertySetterCallback, V8TestInterface5::namedPropertyQueryCallback, V8TestInterface5::namedPropertyDeleterCallback, V8TestInterface5::namedPropertyEnumeratorCallback, v8::Local<v8::Value>(), static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kOnlyInterceptStrings) | int(v8::PropertyHandlerFlags::kNonMasking)));
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.h b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.h
index 80019b5..e7783c5 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.h
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.h
@@ -97,6 +97,7 @@
   MODULES_EXPORT static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>&);
   MODULES_EXPORT static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
   MODULES_EXPORT static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>&);
+  MODULES_EXPORT static void indexedPropertyDefinerCallback(uint32_t index, const v8::PropertyDescriptor&, const v8::PropertyCallbackInfo<v8::Value>&);
 
   static void InstallRuntimeEnabledFeaturesOnTemplate(
       v8::Isolate*,
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index c70683b..a80dfd1 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -963,34 +963,39 @@
   // Calculation of absolute caret bounds requires clean layout.
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-  LayoutRect rect;
+  const VisibleSelection& selection = ComputeVisibleSelectionInDOMTree();
+  if (selection.GetSelectionType() == kNoSelection)
+    return;
 
-  switch (ComputeVisibleSelectionInDOMTree().GetSelectionType()) {
-    case kNoSelection:
-      return;
+  LayoutRect rect;
+  switch (selection.GetSelectionType()) {
     case kCaretSelection:
       rect = LayoutRect(AbsoluteCaretBounds());
       break;
-    case kRangeSelection:
+    case kRangeSelection: {
       rect = LayoutRect(
           reveal_extent_option == kRevealExtent
-              ? AbsoluteCaretBoundsOf(CreateVisiblePosition(
-                    ComputeVisibleSelectionInDOMTree().Extent()))
+              ? AbsoluteCaretBoundsOf(CreateVisiblePosition(selection.Extent()))
               : AbsoluteSelectionBoundsOf(ComputeVisibleSelectionInFlatTree()));
       break;
+    }
+    default:
+      NOTREACHED();
+      break;
   }
 
-  Position start = ComputeVisibleSelectionInDOMTree().Start();
+  // FIXME: This code only handles scrolling the startContainer's layer, but
+  // the selection rect could intersect more than just that.
+  if (DocumentLoader* document_loader = frame_->Loader().GetDocumentLoader())
+    document_loader->GetInitialScrollState().was_scrolled_by_user = true;
+  const Position& start = selection.Start();
   DCHECK(start.AnchorNode());
-  if (start.AnchorNode() && start.AnchorNode()->GetLayoutObject()) {
-    // FIXME: This code only handles scrolling the startContainer's layer, but
-    // the selection rect could intersect more than just that.
-    if (DocumentLoader* document_loader = frame_->Loader().GetDocumentLoader())
-      document_loader->GetInitialScrollState().was_scrolled_by_user = true;
-    if (start.AnchorNode()->GetLayoutObject()->ScrollRectToVisible(
-            rect, alignment, alignment))
-      UpdateAppearance();
-  }
+  DCHECK(start.AnchorNode()->GetLayoutObject());
+  if (!start.AnchorNode()->GetLayoutObject()->ScrollRectToVisible(
+          rect, alignment, alignment))
+    return;
+
+  UpdateAppearance();
 }
 
 void FrameSelection::SetSelectionFromNone() {
diff --git a/third_party/WebKit/Source/core/editing/SelectionModifier.cpp b/third_party/WebKit/Source/core/editing/SelectionModifier.cpp
index 8f61fe1..36b7d35 100644
--- a/third_party/WebKit/Source/core/editing/SelectionModifier.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionModifier.cpp
@@ -178,17 +178,24 @@
       SkipWhitespace(position_after_current_word.DeepEquivalent()));
 }
 
-static void AdjustPositionForUserSelectAll(VisiblePosition& pos,
-                                           bool is_forward) {
-  if (Node* root_user_select_all = EditingStrategy::RootUserSelectAllForNode(
-          pos.DeepEquivalent().AnchorNode()))
-    pos = CreateVisiblePosition(
-        is_forward ? MostForwardCaretPosition(
-                         Position::AfterNode(*root_user_select_all),
-                         kCanCrossEditingBoundary)
-                   : MostBackwardCaretPosition(
-                         Position::BeforeNode(*root_user_select_all),
-                         kCanCrossEditingBoundary));
+static VisiblePosition AdjustForwardPositionForUserSelectAll(
+    VisiblePosition& position) {
+  Node* const root_user_select_all = EditingStrategy::RootUserSelectAllForNode(
+      position.DeepEquivalent().AnchorNode());
+  if (!root_user_select_all)
+    return position;
+  return CreateVisiblePosition(MostForwardCaretPosition(
+      Position::AfterNode(*root_user_select_all), kCanCrossEditingBoundary));
+}
+
+static VisiblePosition AdjustBackwardPositionForUserSelectAll(
+    VisiblePosition& position) {
+  Node* const root_user_select_all = EditingStrategy::RootUserSelectAllForNode(
+      position.DeepEquivalent().AnchorNode());
+  if (!root_user_select_all)
+    return position;
+  return CreateVisiblePosition(MostBackwardCaretPosition(
+      Position::BeforeNode(*root_user_select_all), kCanCrossEditingBoundary));
 }
 
 VisiblePosition SelectionModifier::ModifyExtendingRight(
@@ -231,9 +238,9 @@
       pos = ModifyExtendingForward(granularity);
       break;
   }
-  AdjustPositionForUserSelectAll(
-      pos, DirectionOfEnclosingBlock() == TextDirection::kLtr);
-  return pos;
+  if (DirectionOfEnclosingBlock() == TextDirection::kLtr)
+    return AdjustForwardPositionForUserSelectAll(pos);
+  return AdjustBackwardPositionForUserSelectAll(pos);
 }
 
 VisiblePosition SelectionModifier::ModifyExtendingForward(
@@ -275,9 +282,9 @@
         pos = EndOfDocument(pos);
       break;
   }
-  AdjustPositionForUserSelectAll(
-      pos, DirectionOfEnclosingBlock() == TextDirection::kLtr);
-  return pos;
+  if (DirectionOfEnclosingBlock() == TextDirection::kLtr)
+    return AdjustForwardPositionForUserSelectAll(pos);
+  return AdjustBackwardPositionForUserSelectAll(pos);
 }
 
 VisiblePosition SelectionModifier::ModifyMovingRight(
@@ -410,9 +417,9 @@
       pos = ModifyExtendingBackward(granularity);
       break;
   }
-  AdjustPositionForUserSelectAll(
-      pos, !(DirectionOfEnclosingBlock() == TextDirection::kLtr));
-  return pos;
+  if (DirectionOfEnclosingBlock() == TextDirection::kLtr)
+    return AdjustBackwardPositionForUserSelectAll(pos);
+  return AdjustForwardPositionForUserSelectAll(pos);
 }
 
 VisiblePosition SelectionModifier::ModifyExtendingBackward(
@@ -459,9 +466,9 @@
         pos = StartOfDocument(pos);
       break;
   }
-  AdjustPositionForUserSelectAll(
-      pos, !(DirectionOfEnclosingBlock() == TextDirection::kLtr));
-  return pos;
+  if (DirectionOfEnclosingBlock() == TextDirection::kLtr)
+    return AdjustBackwardPositionForUserSelectAll(pos);
+  return AdjustForwardPositionForUserSelectAll(pos);
 }
 
 VisiblePosition SelectionModifier::ModifyMovingLeft(
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index 0f86edb..6d83f2a 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -118,18 +118,6 @@
   return child ? child->DomWindow() : nullptr;
 }
 
-bool DOMWindow::AnonymousIndexedSetter(uint32_t index,
-                                       const ScriptValue& value) {
-  // https://html.spec.whatwg.org/C/browsers.html#windowproxy-defineownproperty
-  //   step 2 - 1. If P is an array index property name, return false.
-  //
-  // As an alternative way to implement WindowProxy.[[DefineOwnProperty]] for
-  // array index property names, we always intercept and ignore the set
-  // operation for indexed properties, i.e. [[DefineOwnProperty]] for array
-  // index property names has always no effect.
-  return true;  // Intercept unconditionally but do nothing.
-}
-
 bool DOMWindow::IsCurrentlyDisplayedInFrame() const {
   if (GetFrame())
     SECURITY_CHECK(GetFrame()->DomWindow() == this);
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.h b/third_party/WebKit/Source/core/frame/DOMWindow.h
index 0312de7..3146b000 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.h
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.h
@@ -22,7 +22,6 @@
 class LocalDOMWindow;
 class Location;
 class MessageEvent;
-class ScriptValue;
 class SerializedScriptValue;
 class WindowProxyManager;
 
@@ -89,7 +88,6 @@
 
   // Indexed properties
   DOMWindow* AnonymousIndexedGetter(uint32_t index) const;
-  bool AnonymousIndexedSetter(uint32_t index, const ScriptValue&);
 
   void postMessage(PassRefPtr<SerializedScriptValue> message,
                    const MessagePortArray&,
diff --git a/third_party/WebKit/Source/core/frame/Window.idl b/third_party/WebKit/Source/core/frame/Window.idl
index b251a243..f1b8629 100644
--- a/third_party/WebKit/Source/core/frame/Window.idl
+++ b/third_party/WebKit/Source/core/frame/Window.idl
@@ -68,8 +68,6 @@
     // indexed properties
     // https://html.spec.whatwg.org/C/browsers.html#windowproxy-getownproperty
     [NotEnumerable, CrossOrigin] getter Window (unsigned long index);
-    // https://html.spec.whatwg.org/C/browsers.html#windowproxy-defineownproperty
-    setter void (unsigned long index, any value);
 
     // named properties
     [Custom, NotEnumerable, CrossOrigin] getter object (DOMString name);
diff --git a/third_party/WebKit/Source/core/loader/BaseFetchContext.cpp b/third_party/WebKit/Source/core/loader/BaseFetchContext.cpp
index e1dc576..de64a5b 100644
--- a/third_party/WebKit/Source/core/loader/BaseFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/BaseFetchContext.cpp
@@ -227,10 +227,7 @@
   }
 
   if (type == Resource::kScript || type == Resource::kImportResource) {
-    if (GetContentSettingsClient() &&
-        !GetContentSettingsClient()->AllowScriptFromSource(
-            !GetSettings() || GetSettings()->GetScriptEnabled(), url)) {
-      GetContentSettingsClient()->DidNotAllowScript();
+    if (!AllowScriptFromSource(url)) {
       // TODO(estark): Use a different ResourceRequestBlockedReason here, since
       // this check has nothing to do with CSP. https://crbug.com/600795
       return ResourceRequestBlockedReason::CSP;
diff --git a/third_party/WebKit/Source/core/loader/BaseFetchContext.h b/third_party/WebKit/Source/core/loader/BaseFetchContext.h
index 8015dc5..d843291 100644
--- a/third_party/WebKit/Source/core/loader/BaseFetchContext.h
+++ b/third_party/WebKit/Source/core/loader/BaseFetchContext.h
@@ -18,10 +18,8 @@
 namespace blink {
 
 class ConsoleMessage;
-class ContentSettingsClient;
 class KURL;
 class SecurityOrigin;
-class Settings;
 class SubresourceFilter;
 
 // A core-level implementaiton of FetchContext that does not depend on
@@ -57,10 +55,8 @@
   virtual void CountDeprecation(WebFeature) const = 0;
 
  protected:
-  // Used for security checks. It is valid that they return nullptr,
-  // while returning nullptr may result in disable some security checks.
-  virtual ContentSettingsClient* GetContentSettingsClient() const = 0;
-  virtual Settings* GetSettings() const = 0;
+  // Used for security checks.
+  virtual bool AllowScriptFromSource(const KURL&) const = 0;
   virtual SubresourceFilter* GetSubresourceFilter() const = 0;
 
   // Note: subclasses are expected to override following methods.
diff --git a/third_party/WebKit/Source/core/loader/BaseFetchContextTest.cpp b/third_party/WebKit/Source/core/loader/BaseFetchContextTest.cpp
index 0dda279..eef0b19 100644
--- a/third_party/WebKit/Source/core/loader/BaseFetchContextTest.cpp
+++ b/third_party/WebKit/Source/core/loader/BaseFetchContextTest.cpp
@@ -45,10 +45,7 @@
 
   // BaseFetchContext overrides:
   KURL GetFirstPartyForCookies() const override { return KURL(); }
-  ContentSettingsClient* GetContentSettingsClient() const override {
-    return nullptr;
-  }
-  Settings* GetSettings() const override { return nullptr; }
+  bool AllowScriptFromSource(const KURL&) const { return false; }
   SubresourceFilter* GetSubresourceFilter() const override { return nullptr; }
   bool ShouldBlockRequestByInspector(const ResourceRequest&) const override {
     return false;
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 16c4ab0..811342a 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -298,8 +298,7 @@
   if (IsReloadLoadType(MasterDocumentLoader()->LoadType()))
     request.ClearHTTPHeaderField("Save-Data");
 
-  if (GetFrame()->GetSettings() &&
-      GetFrame()->GetSettings()->GetDataSaverEnabled())
+  if (GetSettings() && GetSettings()->GetDataSaverEnabled())
     request.SetHTTPHeaderField("Save-Data", "on");
 
   if (GetLocalFrameClient()->IsClientLoFiActiveForFrame()) {
@@ -889,17 +888,15 @@
       ->Archive();
 }
 
-ContentSettingsClient* FrameFetchContext::GetContentSettingsClient() const {
-  if (IsDetached())
-    return nullptr;
-  return GetFrame()->GetContentSettingsClient();
-}
-
-Settings* FrameFetchContext::GetSettings() const {
-  if (IsDetached())
-    return nullptr;
-  DCHECK(GetFrame());
-  return GetFrame()->GetSettings();
+bool FrameFetchContext::AllowScriptFromSource(const KURL& url) const {
+  ContentSettingsClient* settings_client = GetContentSettingsClient();
+  Settings* settings = GetSettings();
+  if (settings_client && !settings_client->AllowScriptFromSource(
+                             !settings || settings->GetScriptEnabled(), url)) {
+    settings_client->DidNotAllowScript();
+    return false;
+  }
+  return true;
 }
 
 SubresourceFilter* FrameFetchContext::GetSubresourceFilter() const {
@@ -1039,6 +1036,19 @@
     document_->AddConsoleMessage(message);
 }
 
+ContentSettingsClient* FrameFetchContext::GetContentSettingsClient() const {
+  if (IsDetached())
+    return nullptr;
+  return GetFrame()->GetContentSettingsClient();
+}
+
+Settings* FrameFetchContext::GetSettings() const {
+  if (IsDetached())
+    return nullptr;
+  DCHECK(GetFrame());
+  return GetFrame()->GetSettings();
+}
+
 String FrameFetchContext::GetUserAgent() const {
   if (IsDetached())
     return frozen_state_->user_agent;
@@ -1098,6 +1108,17 @@
   } else {
     task_runner = GetTaskRunner();
   }
+
+  if (MasterDocumentLoader()->GetServiceWorkerNetworkProvider()) {
+    WrappedResourceRequest webreq(request);
+    auto loader =
+        MasterDocumentLoader()
+            ->GetServiceWorkerNetworkProvider()
+            ->CreateURLLoader(webreq, task_runner->ToSingleThreadTaskRunner());
+    if (loader)
+      return loader;
+  }
+
   return GetFrame()->CreateURLLoader(request, task_runner.Get());
 }
 
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.h b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
index d59f33e3..a9d3315 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.h
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
@@ -52,6 +52,7 @@
 class LocalFrameClient;
 class ResourceError;
 class ResourceResponse;
+class Settings;
 class WebTaskRunner;
 
 class CORE_EXPORT FrameFetchContext final : public BaseFetchContext {
@@ -184,8 +185,7 @@
 
   // BaseFetchContext overrides:
   KURL GetFirstPartyForCookies() const override;
-  ContentSettingsClient* GetContentSettingsClient() const override;
-  Settings* GetSettings() const override;
+  bool AllowScriptFromSource(const KURL&) const override;
   SubresourceFilter* GetSubresourceFilter() const override;
   bool ShouldBlockRequestByInspector(const ResourceRequest&) const override;
   void DispatchDidBlockRequest(const ResourceRequest&,
@@ -210,6 +210,8 @@
   const ContentSecurityPolicy* GetContentSecurityPolicy() const override;
   void AddConsoleMessage(ConsoleMessage*) const override;
 
+  ContentSettingsClient* GetContentSettingsClient() const;
+  Settings* GetSettings() const;
   String GetUserAgent() const;
   RefPtr<SecurityOrigin> GetRequestorOrigin();
   RefPtr<SecurityOrigin> GetRequestorOriginForFrameLoading();
diff --git a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
index 8dd557a..4160cfa2 100644
--- a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
@@ -93,14 +93,14 @@
   return web_context_->FirstPartyForCookies();
 }
 
-ContentSettingsClient* WorkerFetchContext::GetContentSettingsClient() const {
-  // TODO(horo): Implement this.
-  return nullptr;
-}
-
-Settings* WorkerFetchContext::GetSettings() const {
-  // TODO(horo): Implement this.
-  return nullptr;
+bool WorkerFetchContext::AllowScriptFromSource(const KURL&) const {
+  // Currently we don't use WorkerFetchContext for loading scripts. So this
+  // method must not be called.
+  // TODO(horo): When we will use WorkerFetchContext for loading scripts, we
+  // need to have a copy the script rules of RendererContentSettingRules on the
+  // worker thread.
+  NOTREACHED();
+  return false;
 }
 
 SubresourceFilter* WorkerFetchContext::GetSubresourceFilter() const {
diff --git a/third_party/WebKit/Source/core/loader/WorkerFetchContext.h b/third_party/WebKit/Source/core/loader/WorkerFetchContext.h
index 4ef5bba4..6cd9bea4 100644
--- a/third_party/WebKit/Source/core/loader/WorkerFetchContext.h
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.h
@@ -37,8 +37,7 @@
 
   // BaseFetchContext implementation:
   KURL GetFirstPartyForCookies() const override;
-  ContentSettingsClient* GetContentSettingsClient() const override;
-  Settings* GetSettings() const override;
+  bool AllowScriptFromSource(const KURL&) const override;
   SubresourceFilter* GetSubresourceFilter() const override;
   bool ShouldBlockRequestByInspector(const ResourceRequest&) const override;
   void DispatchDidBlockRequest(const ResourceRequest&,
diff --git a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
index b3075646..75aaa58 100644
--- a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp
@@ -50,6 +50,12 @@
 // that we would ever need. The current UMA stats indicates that this is, in
 // fact, probably too small. There are Android devices out there with a size of
 // 8000 or so.  We might need to make this larger. See: crbug.com/670747
+// TODO(andrew.macpherson): This either needs to be bigger since some OSes allow
+// buffer sizes of 8192 via latencyHint now or else we need to do some
+// validation of the latencyHint 'exact' size before passing it to
+// CreateAudioDevice. Clamping may be tricky though as the buffer size is
+// dependent on the sample rate for some platforms and we're passing in a time
+// value and not a buffer size in the latencyHint. See: crbug.com/737047
 const size_t kFIFOSize = 8192;
 
 std::unique_ptr<AudioDestination> AudioDestination::Create(
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
index 3092806..dc2a9d3 100644
--- a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h
@@ -31,6 +31,10 @@
 #ifndef WebServiceWorkerNetworkProvider_h
 #define WebServiceWorkerNetworkProvider_h
 
+#include <memory>
+
+#include "public/platform/WebURLLoader.h"
+
 namespace blink {
 
 class WebURLRequest;
@@ -61,6 +65,16 @@
   // Returns an identifier of the service worker controlling the document
   // associated with the WebDataSource.
   virtual int64_t ServiceWorkerID() { return -1; }
+
+  // Returns a URLLoader for the associated context. May return nullptr
+  // if this doesn't provide a ServiceWorker specific URLLoader.
+  // Currently this returns non-null only for a controller worker case
+  // and only if servicification is enabled.
+  virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
+      const WebURLRequest& request,
+      base::SingleThreadTaskRunner* task_runner) {
+    return nullptr;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 8b6c8a6..32fb755 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-8-61
-Revision: c56d8851ea987023cc73981a70d261b3f6427545
+Version: VER-2-8-64
+Revision: cf8d9b4ce3fa2c6cd9ccb25585bc17a355c987b0
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 31c5963..d0fcbaf 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36138,6 +36138,12 @@
   <int value="3" label="CUSTOM_PASSPHRASE"/>
 </enum>
 
+<enum name="SyncPasswordHashChange">
+  <int value="0" label="Saved on Chrome sign-in"/>
+  <int value="1" label="Saved in the content area"/>
+  <int value="2" label="Cleared on Chrome sign-out"/>
+</enum>
+
 <enum name="SyncPromoAction">
   <int value="0" label="The promo was shown to the user"/>
   <int value="1" label="The user clicked the promo to sign in"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f2805470..6c1a5189 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -52829,6 +52829,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.SyncPasswordHashChange"
+    enum="SyncPasswordHashChange">
+  <owner>dvadym@chromium.org</owner>
+  <owner>nparker@chromium.org</owner>
+  <summary>
+    This metric reports a type of a sync password hash change event. Recorded
+    when a sync password hash is saved or cleared.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.TimesGeneratedPasswordUsed">
   <obsolete>
     Deprecated as of 11/11/14. New statistic is
@@ -71261,6 +71271,14 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.AndroidGetAccountsTimeUiThread" units="ms">
+  <owner>bsazonov@chromium.org</owner>
+  <summary>
+    The time it takes to retrieve the list of accounts from the system on the UI
+    thread.
+  </summary>
+</histogram>
+
 <histogram name="Signin.AndroidSigninPromoAction"
     enum="AndroidSigninPromoAction">
   <obsolete>
@@ -88238,6 +88256,7 @@
   <suffix name="AccountManager" label="Using Android AccountManager API"/>
   <suffix name="GoogleAuthUtil" label="Using GoogleAuthUtil API"/>
   <affected-histogram name="Signin.AndroidGetAccountsTime"/>
+  <affected-histogram name="Signin.AndroidGetAccountsTimeUiThread"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AndroidTabPersistentStoreTime" separator=".">
@@ -94438,6 +94457,8 @@
       name="PageLoad.Clients.ServiceWorker.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker.PaintTiming.ParseStartToFirstContentfulPaint"/>
+  <affected-histogram
+      name="PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="PageLoadMetricsClientsSubresourceFilter"
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index 99940ddeb..377026c 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -268,7 +268,6 @@
         "views/message_center_view_unittest.cc",
         "views/message_list_view_unittest.cc",
         "views/message_popup_collection_unittest.cc",
-        "views/notification_view_md_unittest.cc",
         "views/notification_view_unittest.cc",
         "views/notifier_settings_view_unittest.cc",
       ]
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 93c99752c..5426c6f 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -117,8 +117,6 @@
   explicit ItemView(const message_center::NotificationItem& item);
   ~ItemView() override;
 
-  const char* GetClassName() const override;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(ItemView);
 };
@@ -145,10 +143,6 @@
 
 ItemView::~ItemView() = default;
 
-const char* ItemView::GetClassName() const {
-  return "ItemView";
-}
-
 // CompactTitleMessageView /////////////////////////////////////////////////////
 
 // CompactTitleMessageView shows notification title and message in a single
@@ -158,8 +152,6 @@
   explicit CompactTitleMessageView();
   ~CompactTitleMessageView() override;
 
-  const char* GetClassName() const override;
-
   void OnPaint(gfx::Canvas* canvas) override;
 
   void set_title(const base::string16& title) { title_ = title; }
@@ -175,11 +167,7 @@
   views::Label* message_view_ = nullptr;
 };
 
-CompactTitleMessageView::~CompactTitleMessageView() = default;
-
-const char* CompactTitleMessageView::GetClassName() const {
-  return "CompactTitleMessageView";
-}
+CompactTitleMessageView::~CompactTitleMessageView() {}
 
 CompactTitleMessageView::CompactTitleMessageView() {
   SetLayoutManager(new views::FillLayout());
@@ -242,7 +230,6 @@
   ~NotificationButtonMD() override;
 
   void SetText(const base::string16& text) override;
-  const char* GetClassName() const override;
 
   std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
       const override;
@@ -273,10 +260,6 @@
   views::LabelButton::SetText(base::i18n::ToUpper(text));
 }
 
-const char* NotificationButtonMD::GetClassName() const {
-  return "NotificationButtonMD";
-}
-
 std::unique_ptr<views::InkDropHighlight>
 NotificationButtonMD::CreateInkDropHighlight() const {
   std::unique_ptr<views::InkDropHighlight> highlight =
@@ -332,12 +315,10 @@
   CreateOrUpdateIconView(notification);
   CreateOrUpdateSmallIconView(notification);
   CreateOrUpdateImageView(notification);
+  CreateOrUpdateActionButtonViews(notification);
   CreateOrUpdateCloseButtonView(notification);
   CreateOrUpdateSettingsButtonView(notification);
   UpdateViewForExpandedState(expanded_);
-  // Should be called at the last because SynthesizeMouseMoveEvent() requires
-  // everything is in the right location when called.
-  CreateOrUpdateActionButtonViews(notification);
 }
 
 NotificationViewMD::NotificationViewMD(MessageCenterController* controller,
@@ -502,10 +483,8 @@
 
 void NotificationViewMD::CreateOrUpdateTitleView(
     const Notification& notification) {
-  if (notification.title().empty() ||
-      notification.type() == NOTIFICATION_TYPE_PROGRESS) {
-    if (title_view_)
-      left_content_->RemoveChildView(title_view_);
+  if (notification.type() == NOTIFICATION_TYPE_PROGRESS) {
+    left_content_->RemoveChildView(title_view_);
     title_view_ = nullptr;
     return;
   }
@@ -560,8 +539,7 @@
 void NotificationViewMD::CreateOrUpdateCompactTitleMessageView(
     const Notification& notification) {
   if (notification.type() != NOTIFICATION_TYPE_PROGRESS) {
-    if (compact_title_message_view_)
-      left_content_->RemoveChildView(compact_title_message_view_);
+    left_content_->RemoveChildView(compact_title_message_view_);
     compact_title_message_view_ = nullptr;
     return;
   }
@@ -578,8 +556,7 @@
 void NotificationViewMD::CreateOrUpdateProgressBarView(
     const Notification& notification) {
   if (notification.type() != NOTIFICATION_TYPE_PROGRESS) {
-    if (progress_bar_view_)
-      left_content_->RemoveChildView(progress_bar_view_);
+    left_content_->RemoveChildView(progress_bar_view_);
     progress_bar_view_ = nullptr;
     header_row_->ClearProgress();
     return;
@@ -627,8 +604,7 @@
     const Notification& notification) {
   if (notification.type() == NOTIFICATION_TYPE_PROGRESS ||
       notification.type() == NOTIFICATION_TYPE_MULTIPLE) {
-    if (icon_view_)
-      right_content_->RemoveChildView(icon_view_);
+    right_content_->RemoveChildView(icon_view_);
     icon_view_ = nullptr;
     return;
   }
@@ -720,14 +696,11 @@
     }
   }
 
-  // Inherit mouse hover state when action button views reset.
-  // If the view is not expanded, there should be no hover state.
-  if (new_buttons && expanded_) {
+  if (new_buttons) {
+    // TODO(fukino): Investigate if this Layout() is necessary.
+    Layout();
     views::Widget* widget = GetWidget();
-    if (widget) {
-      // This Layout() is needed because button should be in the right location
-      // in the view hierarchy when SynthesizeMouseMoveEvent() is called.
-      Layout();
+    if (widget != NULL) {
       widget->SetSize(widget->GetContentsView()->GetPreferredSize());
       GetWidget()->SynthesizeMouseMoveEvent();
     }
diff --git a/ui/message_center/views/notification_view_md.h b/ui/message_center/views/notification_view_md.h
index 547d112d..890165a 100644
--- a/ui/message_center/views/notification_view_md.h
+++ b/ui/message_center/views/notification_view_md.h
@@ -64,13 +64,6 @@
   views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, CreateOrUpdateTest);
-  FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestIconSizing);
-  FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonsStateTest);
-  FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonCountTest);
-
-  friend class NotificationViewMDTest;
-
   void CreateOrUpdateViews(const Notification& notification);
 
   void CreateOrUpdateContextTitleView(const Notification& notification);
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc
deleted file mode 100644
index 9baa68c..0000000
--- a/ui/message_center/views/notification_view_md_unittest.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/message_center/views/notification_view_md.h"
-
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/events/event_processor.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/test/event_generator.h"
-#include "ui/gfx/canvas.h"
-#include "ui/message_center/message_center_style.h"
-#include "ui/message_center/views/message_center_controller.h"
-#include "ui/message_center/views/notification_header_view.h"
-#include "ui/message_center/views/proportional_image_view.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/controls/button/label_button.h"
-#include "ui/views/test/views_test_base.h"
-#include "ui/views/test/widget_test.h"
-
-namespace message_center {
-
-/* Test fixture ***************************************************************/
-
-// Used to fill bitmaps returned by CreateBitmap().
-static const SkColor kBitmapColor = SK_ColorGREEN;
-
-class NotificationViewMDTest : public views::ViewsTestBase,
-                               public MessageCenterController {
- public:
-  NotificationViewMDTest();
-  ~NotificationViewMDTest() override;
-
-  // Overridden from ViewsTestBase:
-  void SetUp() override;
-  void TearDown() override;
-
-  // Overridden from MessageCenterController:
-  void ClickOnNotification(const std::string& notification_id) override;
-  void RemoveNotification(const std::string& notification_id,
-                          bool by_user) override;
-  std::unique_ptr<ui::MenuModel> CreateMenuModel(
-      const NotifierId& notifier_id,
-      const base::string16& display_source) override;
-  bool HasClickedListener(const std::string& notification_id) override;
-  void ClickOnNotificationButton(const std::string& notification_id,
-                                 int button_index) override;
-  void ClickOnSettingsButton(const std::string& notification_id) override;
-  void UpdateNotificationSize(const std::string& notification_id) override;
-
-  NotificationViewMD* notification_view() const {
-    return notification_view_.get();
-  }
-  Notification* notification() const { return notification_.get(); }
-  views::Widget* widget() const {
-    DCHECK_EQ(widget_, notification_view()->GetWidget());
-    return widget_;
-  }
-
- protected:
-  const gfx::Image CreateTestImage(int width, int height);
-  const SkBitmap CreateBitmap(int width, int height);
-  std::vector<ButtonInfo> CreateButtons(int number);
-
-  // Paints |view| and returns the size that the original image (which must have
-  // been created by CreateBitmap()) was scaled to.
-  gfx::Size GetImagePaintSize(ProportionalImageView* view);
-
-  void UpdateNotificationViews();
-  float GetNotificationSlideAmount() const;
-  bool IsRemoved(const std::string& notification_id) const;
-  void DispatchGesture(const ui::GestureEventDetails& details);
-  void BeginScroll();
-  void EndScroll();
-  void ScrollBy(int dx);
-  views::ImageButton* GetCloseButton();
-
- private:
-  std::set<std::string> removed_ids_;
-
-  std::unique_ptr<RichNotificationData> data_;
-  std::unique_ptr<Notification> notification_;
-  std::unique_ptr<NotificationViewMD> notification_view_;
-  views::Widget* widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationViewMDTest);
-};
-
-NotificationViewMDTest::NotificationViewMDTest() = default;
-NotificationViewMDTest::~NotificationViewMDTest() = default;
-
-void NotificationViewMDTest::SetUp() {
-  views::ViewsTestBase::SetUp();
-  // Create a dummy notification.
-  data_.reset(new RichNotificationData());
-  notification_.reset(new Notification(
-      NOTIFICATION_TYPE_BASE_FORMAT, std::string("notification id"),
-      base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message"),
-      CreateTestImage(80, 80), base::UTF8ToUTF16("display source"), GURL(),
-      NotifierId(NotifierId::APPLICATION, "extension_id"), *data_, nullptr));
-  notification_->set_small_image(CreateTestImage(16, 16));
-  notification_->set_image(CreateTestImage(320, 240));
-
-  // Then create a new NotificationView with that single notification.
-  // In the actual code path, this is instantiated by
-  // MessageViewFactory::Create.
-  // TODO(tetsui): Confirm that NotificationViewMD options are same as one
-  // created by the method.
-  notification_view_.reset(new NotificationViewMD(this, *notification_));
-  notification_view_->SetIsNested();
-  notification_view_->set_owned_by_client();
-
-  views::Widget::InitParams init_params(
-      CreateParams(views::Widget::InitParams::TYPE_POPUP));
-  widget_ = new views::Widget();
-  widget_->Init(init_params);
-  widget_->SetContentsView(notification_view_.get());
-  widget_->SetSize(notification_view_->GetPreferredSize());
-  widget_->Show();
-}
-
-void NotificationViewMDTest::TearDown() {
-  widget()->Close();
-  notification_view_.reset();
-  views::ViewsTestBase::TearDown();
-}
-
-void NotificationViewMDTest::ClickOnNotification(
-    const std::string& notification_id) {
-  // For this test, this method should not be invoked.
-  NOTREACHED();
-}
-
-void NotificationViewMDTest::RemoveNotification(
-    const std::string& notification_id,
-    bool by_user) {
-  removed_ids_.insert(notification_id);
-}
-
-std::unique_ptr<ui::MenuModel> NotificationViewMDTest::CreateMenuModel(
-    const NotifierId& notifier_id,
-    const base::string16& display_source) {
-  // For this test, this method should not be invoked.
-  NOTREACHED();
-  return nullptr;
-}
-
-bool NotificationViewMDTest::HasClickedListener(
-    const std::string& notification_id) {
-  return true;
-}
-
-void NotificationViewMDTest::ClickOnNotificationButton(
-    const std::string& notification_id,
-    int button_index) {
-  // For this test, this method should not be invoked.
-  NOTREACHED();
-}
-
-void NotificationViewMDTest::ClickOnSettingsButton(
-    const std::string& notification_id) {
-  // For this test, this method should not be invoked.
-  NOTREACHED();
-}
-
-void NotificationViewMDTest::UpdateNotificationSize(
-    const std::string& notification_id) {
-  widget()->SetSize(notification_view()->GetPreferredSize());
-}
-
-const gfx::Image NotificationViewMDTest::CreateTestImage(int width,
-                                                         int height) {
-  return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
-}
-
-const SkBitmap NotificationViewMDTest::CreateBitmap(int width, int height) {
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(width, height);
-  bitmap.eraseColor(kBitmapColor);
-  return bitmap;
-}
-
-std::vector<ButtonInfo> NotificationViewMDTest::CreateButtons(int number) {
-  ButtonInfo info(base::ASCIIToUTF16("Test button."));
-  info.icon = CreateTestImage(4, 4);
-  return std::vector<ButtonInfo>(number, info);
-}
-
-gfx::Size NotificationViewMDTest::GetImagePaintSize(
-    ProportionalImageView* view) {
-  CHECK(view);
-  if (view->bounds().IsEmpty())
-    return gfx::Size();
-
-  gfx::Size canvas_size = view->bounds().size();
-  gfx::Canvas canvas(canvas_size, 1.0 /* image_scale */, true /* is_opaque */);
-  static_assert(kBitmapColor != SK_ColorBLACK,
-                "The bitmap color must match the background color");
-  canvas.DrawColor(SK_ColorBLACK);
-  view->OnPaint(&canvas);
-
-  SkBitmap bitmap = canvas.GetBitmap();
-  // Incrementally inset each edge at its midpoint to find the bounds of the
-  // rect containing the image's color. This assumes that the image is
-  // centered in the canvas.
-  const int kHalfWidth = canvas_size.width() / 2;
-  const int kHalfHeight = canvas_size.height() / 2;
-  gfx::Rect rect(canvas_size);
-  while (rect.width() > 0 &&
-         bitmap.getColor(rect.x(), kHalfHeight) != kBitmapColor)
-    rect.Inset(1, 0, 0, 0);
-  while (rect.height() > 0 &&
-         bitmap.getColor(kHalfWidth, rect.y()) != kBitmapColor)
-    rect.Inset(0, 1, 0, 0);
-  while (rect.width() > 0 &&
-         bitmap.getColor(rect.right() - 1, kHalfHeight) != kBitmapColor)
-    rect.Inset(0, 0, 1, 0);
-  while (rect.height() > 0 &&
-         bitmap.getColor(kHalfWidth, rect.bottom() - 1) != kBitmapColor)
-    rect.Inset(0, 0, 0, 1);
-
-  return rect.size();
-}
-
-void NotificationViewMDTest::UpdateNotificationViews() {
-  notification_view()->UpdateWithNotification(*notification());
-}
-
-float NotificationViewMDTest::GetNotificationSlideAmount() const {
-  return notification_view_->GetSlideOutLayer()
-      ->transform()
-      .To2dTranslation()
-      .x();
-}
-
-bool NotificationViewMDTest::IsRemoved(
-    const std::string& notification_id) const {
-  return (removed_ids_.find(notification_id) != removed_ids_.end());
-}
-
-void NotificationViewMDTest::DispatchGesture(
-    const ui::GestureEventDetails& details) {
-  ui::test::EventGenerator generator(
-      notification_view()->GetWidget()->GetNativeWindow());
-  ui::GestureEvent event(0, 0, 0, ui::EventTimeForNow(), details);
-  generator.Dispatch(&event);
-}
-
-void NotificationViewMDTest::BeginScroll() {
-  DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
-}
-
-void NotificationViewMDTest::EndScroll() {
-  DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
-}
-
-void NotificationViewMDTest::ScrollBy(int dx) {
-  DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0));
-}
-
-views::ImageButton* NotificationViewMDTest::GetCloseButton() {
-  return notification_view()->header_row_->close_button();
-}
-
-/* Unit tests *****************************************************************/
-
-// TODO(tetsui): Following tests are not yet ported from NotificationViewTest.
-// * CreateOrUpdateTestSettingsButton
-// * TestLineLimits
-// * TestImageSizing
-// * SettingsButtonTest
-// * ViewOrderingTest
-// * FormatContextMessageTest
-
-TEST_F(NotificationViewMDTest, CreateOrUpdateTest) {
-  EXPECT_NE(nullptr, notification_view()->title_view_);
-  EXPECT_NE(nullptr, notification_view()->message_view_);
-  EXPECT_NE(nullptr, notification_view()->icon_view_);
-  EXPECT_NE(nullptr, notification_view()->image_view_);
-
-  notification()->set_image(gfx::Image());
-  notification()->set_title(base::string16());
-  notification()->set_message(base::string16());
-  notification()->set_icon(gfx::Image());
-
-  notification_view()->CreateOrUpdateViews(*notification());
-
-  EXPECT_EQ(nullptr, notification_view()->title_view_);
-  EXPECT_EQ(nullptr, notification_view()->message_view_);
-  EXPECT_EQ(nullptr, notification_view()->image_view_);
-  // We still expect an icon view for all layouts.
-  EXPECT_NE(nullptr, notification_view()->icon_view_);
-}
-
-TEST_F(NotificationViewMDTest, TestIconSizing) {
-  // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView.
-  const int kNotificationIconSize = 30;
-
-  notification()->set_type(NOTIFICATION_TYPE_SIMPLE);
-  ProportionalImageView* view = notification_view()->icon_view_;
-
-  // Icons smaller than the maximum size should remain unscaled.
-  notification()->set_icon(
-      CreateTestImage(kNotificationIconSize / 2, kNotificationIconSize / 4));
-  UpdateNotificationViews();
-  EXPECT_EQ(gfx::Size(kNotificationIconSize / 2, kNotificationIconSize / 4)
-                .ToString(),
-            GetImagePaintSize(view).ToString());
-
-  // Icons of exactly the intended icon size should remain unscaled.
-  notification()->set_icon(
-      CreateTestImage(kNotificationIconSize, kNotificationIconSize));
-  UpdateNotificationViews();
-  EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
-            GetImagePaintSize(view).ToString());
-
-  // Icons over the maximum size should be scaled down, maintaining proportions.
-  notification()->set_icon(
-      CreateTestImage(2 * kNotificationIconSize, 2 * kNotificationIconSize));
-  UpdateNotificationViews();
-  EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
-            GetImagePaintSize(view).ToString());
-
-  notification()->set_icon(
-      CreateTestImage(4 * kNotificationIconSize, 2 * kNotificationIconSize));
-  UpdateNotificationViews();
-  EXPECT_EQ(
-      gfx::Size(kNotificationIconSize, kNotificationIconSize / 2).ToString(),
-      GetImagePaintSize(view).ToString());
-}
-
-TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
-  notification()->set_buttons(CreateButtons(2));
-  notification_view()->CreateOrUpdateViews(*notification());
-  widget()->Show();
-
-  // Action buttons are hidden by collapsed state.
-  if (!notification_view()->expanded_)
-    notification_view()->ToggleExpanded();
-  EXPECT_TRUE(notification_view()->actions_row_->visible());
-
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[0]->state());
-
-  // Now construct a mouse move event 1 pixel inside the boundary of the action
-  // button.
-  gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
-                                    &cursor_location);
-  ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
-                      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
-  widget()->OnMouseEvent(&move);
-
-  EXPECT_EQ(views::CustomButton::STATE_HOVERED,
-            notification_view()->action_buttons_[0]->state());
-
-  notification_view()->CreateOrUpdateViews(*notification());
-
-  EXPECT_EQ(views::CustomButton::STATE_HOVERED,
-            notification_view()->action_buttons_[0]->state());
-
-  // Now construct a mouse move event 1 pixel outside the boundary of the
-  // widget.
-  cursor_location = gfx::Point(-1, -1);
-  move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
-                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
-  widget()->OnMouseEvent(&move);
-
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[0]->state());
-}
-
-TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
-  notification()->set_buttons(CreateButtons(2));
-  notification_view()->UpdateWithNotification(*notification());
-  widget()->Show();
-
-  // Action buttons are hidden by collapsed state.
-  if (!notification_view()->expanded_)
-    notification_view()->ToggleExpanded();
-  EXPECT_TRUE(notification_view()->actions_row_->visible());
-
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[0]->state());
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[1]->state());
-
-  // Now construct a mouse move event 1 pixel inside the boundary of the action
-  // button.
-  gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToScreen(notification_view()->action_buttons_[0],
-                                    &cursor_location);
-  ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
-                      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
-  ui::EventDispatchDetails details =
-      views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move);
-  EXPECT_FALSE(details.dispatcher_destroyed);
-
-  EXPECT_EQ(views::CustomButton::STATE_HOVERED,
-            notification_view()->action_buttons_[0]->state());
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[1]->state());
-
-  notification()->set_buttons(CreateButtons(1));
-  notification_view()->UpdateWithNotification(*notification());
-
-  EXPECT_EQ(views::CustomButton::STATE_HOVERED,
-            notification_view()->action_buttons_[0]->state());
-  EXPECT_EQ(1u, notification_view()->action_buttons_.size());
-
-  // Now construct a mouse move event 1 pixel outside the boundary of the
-  // widget.
-  cursor_location = gfx::Point(-1, -1);
-  move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
-                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
-  widget()->OnMouseEvent(&move);
-
-  EXPECT_EQ(views::CustomButton::STATE_NORMAL,
-            notification_view()->action_buttons_[0]->state());
-}
-
-TEST_F(NotificationViewMDTest, SlideOut) {
-  ui::ScopedAnimationDurationScaleMode zero_duration_scope(
-      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
-
-  UpdateNotificationViews();
-  std::string notification_id = notification()->id();
-
-  BeginScroll();
-  ScrollBy(-10);
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(-10.f, GetNotificationSlideAmount());
-  EndScroll();
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(0.f, GetNotificationSlideAmount());
-
-  BeginScroll();
-  ScrollBy(-200);
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(-200.f, GetNotificationSlideAmount());
-  EndScroll();
-  EXPECT_TRUE(IsRemoved(notification_id));
-}
-
-TEST_F(NotificationViewMDTest, SlideOutNested) {
-  ui::ScopedAnimationDurationScaleMode zero_duration_scope(
-      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
-
-  UpdateNotificationViews();
-  notification_view()->SetIsNested();
-  std::string notification_id = notification()->id();
-
-  BeginScroll();
-  ScrollBy(-10);
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(-10.f, GetNotificationSlideAmount());
-  EndScroll();
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(0.f, GetNotificationSlideAmount());
-
-  BeginScroll();
-  ScrollBy(-200);
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_EQ(-200.f, GetNotificationSlideAmount());
-  EndScroll();
-  EXPECT_TRUE(IsRemoved(notification_id));
-}
-
-// Pinning notification is ChromeOS only feature.
-#if defined(OS_CHROMEOS)
-
-TEST_F(NotificationViewMDTest, SlideOutPinned) {
-  ui::ScopedAnimationDurationScaleMode zero_duration_scope(
-      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
-
-  notification()->set_pinned(true);
-  UpdateNotificationViews();
-  std::string notification_id = notification()->id();
-
-  BeginScroll();
-  ScrollBy(-200);
-  EXPECT_FALSE(IsRemoved(notification_id));
-  EXPECT_LT(-200.f, GetNotificationSlideAmount());
-  EndScroll();
-  EXPECT_FALSE(IsRemoved(notification_id));
-}
-
-TEST_F(NotificationViewMDTest, Pinned) {
-  notification()->set_pinned(true);
-
-  UpdateNotificationViews();
-  EXPECT_FALSE(GetCloseButton()->visible());
-}
-
-#endif  // defined(OS_CHROMEOS)
-
-}  // namespace message_center