diff --git a/DEPS b/DEPS
index 388e26b..1094d701 100644
--- a/DEPS
+++ b/DEPS
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '2bf942d8c21b653efdfdcae681769cffbfaa0663',
+  'pdfium_revision': 'ab5939e1ad77f0d4fcdb0abc258da1e6fb7eef1a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '57e600c76c9f2f6ab3a5b82d3cc21ca738a62a7e',
+  'catapult_revision': 'ee4b073833a40a8ebaf58ea3f6eff8195884bc64',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/base/synchronization/atomic_flag_unittest.cc b/base/synchronization/atomic_flag_unittest.cc
index 76e5d968..a7c4d9c 100644
--- a/base/synchronization/atomic_flag_unittest.cc
+++ b/base/synchronization/atomic_flag_unittest.cc
@@ -89,7 +89,8 @@
 
   // Use |reset_flag| to confirm that the above completed (which the rest of
   // this test assumes).
-  ASSERT_TRUE(reset_flag.IsSet());
+  while (!reset_flag.IsSet())
+    PlatformThread::YieldCurrentThread();
 
   tested_flag.UnsafeResetForTesting();
   EXPECT_FALSE(tested_flag.IsSet());
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h
index 2e436ce8..6aa6418 100644
--- a/base/task_scheduler/task_scheduler.h
+++ b/base/task_scheduler/task_scheduler.h
@@ -10,6 +10,7 @@
 
 #include "base/base_export.h"
 #include "base/callback.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
@@ -25,6 +26,12 @@
 class V8Platform;
 }
 
+namespace content {
+// Can't use the FRIEND_TEST_ALL_PREFIXES macro because the test is in a
+// different namespace.
+class BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
+}  // namespace content
+
 namespace tracked_objects {
 class Location;
 }
@@ -56,10 +63,10 @@
             foreground_blocking_worker_pool_params_in);
     ~InitParams();
 
-    const SchedulerWorkerPoolParams background_worker_pool_params;
-    const SchedulerWorkerPoolParams background_blocking_worker_pool_params;
-    const SchedulerWorkerPoolParams foreground_worker_pool_params;
-    const SchedulerWorkerPoolParams foreground_blocking_worker_pool_params;
+    SchedulerWorkerPoolParams background_worker_pool_params;
+    SchedulerWorkerPoolParams background_blocking_worker_pool_params;
+    SchedulerWorkerPoolParams foreground_worker_pool_params;
+    SchedulerWorkerPoolParams foreground_blocking_worker_pool_params;
   };
 
   // Destroying a TaskScheduler is not allowed in production; it is always
@@ -192,6 +199,7 @@
 
  private:
   friend class gin::V8Platform;
+  friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
 
   // Returns the maximum number of non-single-threaded tasks posted with
   // |traits| that can run concurrently in this TaskScheduler.
diff --git a/build/android/adb_gdb b/build/android/adb_gdb
index f9e3726..9b8f5be 100755
--- a/build/android/adb_gdb
+++ b/build/android/adb_gdb
@@ -87,6 +87,7 @@
 ANNOTATE=
 FORCE=
 GDBEXEPOSTFIX=gdb
+CGDB=
 GDBINIT=
 GDBSERVER=
 HELP=
@@ -206,6 +207,12 @@
     --ui)
       GDBEXEPOSTFIX=gdbtui
       ;;
+    --cgdb)
+      CGDB=cgdb
+      ;;
+    --cgdb=*)
+      CGDB=$optarg
+      ;;
     --verbose)
       VERBOSE=$(( $VERBOSE + 1 ))
       ;;
@@ -306,6 +313,7 @@
   --help|-h|-?          Print this message.
   --verbose             Increase verbosity.
 
+  --cgdb[=<file>]       Use cgdb (an interface for gdb that shows the code).
   --sandboxed           Debug first sandboxed process we find.
   --sandboxed=<num>     Debug specific sandboxed process.
   --symbol-dir=<path>   Specify directory with symbol shared libraries.
@@ -1047,5 +1055,9 @@
 fi
 
 log "Launching gdb client: $GDB $GDB_ARGS -x $COMMANDS"
-$GDB $GDB_ARGS -x $COMMANDS &&
+if [ "$CGDB" ]; then
+  $CGDB -d $GDB -- $GDB_ARGS -x $COMMANDS
+else
+  $GDB $GDB_ARGS -x $COMMANDS
+fi
 rm -f "$GDBSERVER_PIDFILE"
diff --git a/chrome/android/java/res/layout/new_tab_page_snippets_card_large_thumbnail.xml b/chrome/android/java/res/layout/new_tab_page_snippets_card_large_thumbnail.xml
new file mode 100644
index 0000000..f59cd8e
--- /dev/null
+++ b/chrome/android/java/res/layout/new_tab_page_snippets_card_large_thumbnail.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:chrome="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/snippets_card_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/card_single"
+    android:foreground="@drawable/button_borderless_compat">
+
+    <LinearLayout
+        android:id="@+id/text_layout"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:layout_toStartOf="@+id/article_thumbnail"
+        android:layout_alignWithParentIfMissing="true"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="@dimen/snippets_padding"
+        android:orientation="vertical">
+
+        <org.chromium.ui.widget.TextViewWithLeading
+            android:id="@+id/article_headline"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:ellipsize="end"
+            android:textSize="16sp"
+            android:textColor="@color/snippets_headline_text_color"
+            chrome:leading="22dp" />
+
+        <org.chromium.ui.widget.TextViewWithLeading
+            android:id="@+id/article_snippet"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:maxLines="2"
+            android:ellipsize="end"
+            android:textSize="14sp"
+            android:textColor="@color/snippets_text_color"
+            chrome:leading="20dp" />
+
+        <LinearLayout
+            tools:ignore="UseCompoundDrawables"
+            android:id="@+id/publisher_bar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/snippets_publisher_margin_top_with_article_snippet"
+            android:orientation="horizontal">
+
+            <!-- The following attributes:
+                 - publisher_bar's android:layout_width="wrap_content"
+                 - article_publisher's android:layout_width="0dp"
+                 - article_publisher's android:layout_weight="1"
+                 - article_publisher's android:ellipsize="end"
+                 - article_age's android:layout_width="wrap_content"
+                 All ensure that when the publisher string is long, it starts to ellipsize before
+                 pushing the article age string and the offline icon off the screen.
+                 See: https://crbug.com/625775 and https://crbug.com/678568 -->
+            <TextView
+                android:id="@+id/article_publisher"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:drawablePadding="8dp"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textSize="12sp"
+                android:textColor="@color/snippets_publisher_name_color"
+                android:textDirection="locale" />
+            <TextView
+                android:id="@+id/article_age"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="1"
+                android:textSize="12sp"
+                android:textColor="@color/snippets_publisher_name_color"
+                android:textDirection="locale"/>
+
+            <!-- We can't add this ImageView as a CompoundDrawable to the TextView because we want to
+                 have different paddings between the favicon (which is a compound drawable on the
+                 TextView) and the offline icon. -->
+            <org.chromium.chrome.browser.widget.TintedImageView
+                android:id="@+id/offline_icon"
+                android:layout_width="@dimen/snippets_offline_icon_size"
+                android:layout_height="@dimen/snippets_offline_icon_size"
+                android:layout_marginStart="6dp"
+                android:alpha="0.54"
+                android:src="@drawable/offline_pin_round"
+                android:contentDescription="@string/accessibility_ntp_offline_badge"
+                android:visibility="gone"
+                chrome:chrometint="#000" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <org.chromium.chrome.browser.widget.TintedImageView
+        android:id="@+id/article_thumbnail"
+        android:layout_width="@dimen/snippets_thumbnail_size_large"
+        android:layout_height="@dimen/snippets_thumbnail_size_large"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentEnd="true"
+        android:scaleType="centerCrop"
+        android:contentDescription="@null"
+        android:src="@null" />
+</RelativeLayout>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 2c56be6..ccd67a6 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -317,6 +317,7 @@
     <dimen name="ntp_sign_in_promo_margin_top">20dp</dimen>
     <dimen name="ntp_progress_indicator_diameter">56dp</dimen>
     <dimen name="snippets_thumbnail_size">72dp</dimen>
+    <dimen name="snippets_thumbnail_size_large">124dp</dimen>
     <dimen name="snippets_thumbnail_margin">16dp</dimen>
     <!-- The default padding for the peeking card and the amount the card peeks by in the current
          peeking card animation (the calculations assume this is the same). -->
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 c690f7bb..05d51edf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -157,6 +157,8 @@
     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";
     public static final String CONTENT_SUGGESTIONS_SHOW_SUMMARY = "ContentSuggestionsShowSummary";
     public static final String CONTEXTUAL_SEARCH_SINGLE_ACTIONS = "ContextualSearchSingleActions";
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 a9163ba..16c96df 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
@@ -21,6 +21,7 @@
 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;
@@ -88,6 +89,7 @@
     private final UiConfig mUiConfig;
     private final ThumbnailProvider mThumbnailProvider;
 
+    private final LinearLayout mTextLayout;
     private final TextView mHeadlineTextView;
     private final TextView mPublisherTextView;
     private final TextView mArticleSnippetTextView;
@@ -119,23 +121,29 @@
     public SnippetArticleViewHolder(SuggestionsRecyclerView parent,
             ContextMenuManager contextMenuManager, SuggestionsUiDelegate uiDelegate,
             UiConfig uiConfig) {
-        super(R.layout.new_tab_page_snippets_card, parent, uiConfig, contextMenuManager);
+        super(ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)
+                        ? R.layout.new_tab_page_snippets_card_large_thumbnail
+                        : R.layout.new_tab_page_snippets_card,
+                parent, uiConfig, contextMenuManager);
 
         mUiDelegate = uiDelegate;
         mUiConfig = uiConfig;
         mThumbnailProvider = uiDelegate.getThumbnailProvider();
 
-        mThumbnailView = (TintedImageView) itemView.findViewById(R.id.article_thumbnail);
-        mThumbnailSize =
-                itemView.getResources().getDimensionPixelSize(R.dimen.snippets_thumbnail_size);
-
+        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);
         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);
         mUseFaviconService = CardsVariationParameters.isFaviconServiceEnabled();
@@ -255,6 +263,9 @@
         boolean showDescription = shouldShowDescription(horizontalStyle, verticalStyle, layout);
         boolean showThumbnail = shouldShowThumbnail(layout);
 
+        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);
@@ -277,8 +288,10 @@
             publisherBarParams.topMargin = 0;
         }
 
-        ApiCompatibilityUtils.setMarginEnd(
-                publisherBarParams, showThumbnail ? mThumbnailFootprintPx : 0);
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_LARGE_THUMBNAIL)) {
+            ApiCompatibilityUtils.setMarginEnd(
+                    publisherBarParams, showThumbnail ? mThumbnailFootprintPx : 0);
+        }
         mPublisherBar.setLayoutParams(publisherBarParams);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
index e1cd08dd..6cb2480c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -252,7 +252,7 @@
         Bitmap bitmap = BitmapFactory.decodeResource(mActivityTestRule.getActivity().getResources(),
                 R.drawable.signin_promo_illustration);
         int thumbnailSize = mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
-                R.dimen.snippets_thumbnail_size);
+                R.dimen.snippets_thumbnail_size_large);
         Bitmap thumbnail = ThumbnailUtils.extractThumbnail(
                 bitmap, thumbnailSize, thumbnailSize, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
         shortSnippet.setThumbnailBitmap(mUiDelegate.getReferencePool().put(thumbnail));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 2607db9c..fc3aed6e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6442,7 +6442,7 @@
           Removing harmful software...
         </message>
         <message name="IDS_CHROME_CLEANUP_WEBUI_TITLE_RESTART" desc="A status message, appearing on the Chrome Cleanup web page, that the cleanup of unwanted software initiated by the user was performed but isn't finished. User must take additional action to finish the cleanup. 'Restart' is imperative. Short for 'To finish removing harmful software...'">
-          To finish, restart your computer
+          To finish removing harmful software, restart your computer
         </message>
       </if>
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d302cd55..4110985 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2358,6 +2358,11 @@
      flag_descriptions::kEnableContentSuggestionsCategoriesDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kContentSuggestionsCategories)},
+    {"enable-content-suggestions-large-thumbnail",
+     flag_descriptions::kEnableContentSuggestionsLargeThumbnailName,
+     flag_descriptions::kEnableContentSuggestionsLargeThumbnailDescription,
+     kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kContentSuggestionsLargeThumbnail)},
     {"enable-content-suggestions-settings",
      flag_descriptions::kEnableContentSuggestionsSettingsName,
      flag_descriptions::kEnableContentSuggestionsSettingsDescription,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index fe45bbc..85c6903f 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -57,6 +57,7 @@
     &kChromeHomeExpandButton,
     &kChromeHomeSwipeLogic,
     &kContentSuggestionsCategories,
+    &kContentSuggestionsLargeThumbnail,
     &kContentSuggestionsSettings,
     &kContentSuggestionsShowSummary,
     &kContextualSearchSingleActions,
@@ -146,6 +147,9 @@
 const base::Feature kContentSuggestionsCategories{
     "ContentSuggestionsCategories", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kContentSuggestionsLargeThumbnail{
+    "ContentSuggestionsLargeThumbnail", base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kContentSuggestionsSettings{
     "ContentSuggestionsSettings", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 98c2b13..7a04a72 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -23,6 +23,7 @@
 extern const base::Feature kChromeHomeExpandButton;
 extern const base::Feature kChromeHomeSwipeLogic;
 extern const base::Feature kContentSuggestionsCategories;
+extern const base::Feature kContentSuggestionsLargeThumbnail;
 extern const base::Feature kContentSuggestionsSettings;
 extern const base::Feature kContentSuggestionsShowSummary;
 extern const base::Feature kContextualSearchSingleActions;
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc
index 521191d..deff8885 100644
--- a/chrome/browser/autocomplete/autocomplete_browsertest.cc
+++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc
@@ -151,7 +151,7 @@
     EXPECT_TRUE(autocomplete_controller->done());
     EXPECT_FALSE(location_bar->GetDestinationURL().is_valid());
     EXPECT_TRUE(omnibox_view->GetText().empty());
-    EXPECT_TRUE(omnibox_view->IsSelectAll());
+    EXPECT_FALSE(omnibox_view->IsSelectAll());
     const AutocompleteResult& result = autocomplete_controller->result();
     ASSERT_GE(result.size(), 1U) << AutocompleteResultAsString(result);
     AutocompleteMatch match = result.match_at(0);
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index c73f5897..09296267 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -2289,7 +2289,7 @@
   // 1- For stopping the hanging page.
   // 2- For completing the load of the above navigation.
   // 3- For completing the load of the login tab.
-  // NOTE: for PlzNaviate the first one doesn't show up.
+  // NOTE: for PlzNavigate the first one doesn't show up.
   test_navigation_observer.WaitForNavigations(
       content::IsBrowserSideNavigationEnabled() ? 2 : 3);
   // Should end up with a captive portal interstitial and a new login tab.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c9876e02..6e37a87 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -9,7 +9,7 @@
 const char kBrowserSideNavigationName[] = "Enable browser side navigation";
 
 const char kBrowserSideNavigationDescription[] =
-    "Enable browser side navigation (aka PlzNaviate).";
+    "Enable browser side navigation (aka PlzNavigate).";
 
 //  Material Design version of chrome://bookmarks
 
@@ -2361,6 +2361,13 @@
     "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.";
+
+const char kEnableContentSuggestionsLargeThumbnailDescription[] =
+    "If enabled, the content suggestions cards will use large thumbnails and "
+    "some related adjustments.";
+
 const char kEnableContentSuggestionsSettingsName[] =
     "Show content suggestions settings.";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 06fac16..b402733 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -898,6 +898,9 @@
 extern const char kEnableContentSuggestionsCategoriesName[];
 extern const char kEnableContentSuggestionsCategoriesDescription[];
 
+extern const char kEnableContentSuggestionsLargeThumbnailName[];
+extern const char kEnableContentSuggestionsLargeThumbnailDescription[];
+
 extern const char kEnableContentSuggestionsSettingsName[];
 extern const char kEnableContentSuggestionsSettingsDescription[];
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index 48bcc7f..5e16510 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -35,7 +35,7 @@
   font-size: 0;
   height: calc(146px + 130px);
   line-height: 146px;
-  margin: 0;
+  margin: 2px 0 0 0;
   opacity: 0;
   position: absolute;
   /* This align correctly for both LTR and RTL */
@@ -45,23 +45,28 @@
 
 .mv-tile,
 .mv-empty-tile {
-  background: rgb(242,242,242);
-  border: 1px solid transparent;
   border-radius: 2px;
   box-sizing: border-box;
   display: inline-block;
   font-family: arial, sans-serif;
   font-size: 12px;
-  height: calc(130px - 2px);
+  height: 128px;
   line-height: 100%;
   margin: 0 8px;
   opacity: 1;
-  outline: 0;
   overflow: hidden;
   position: relative;
   vertical-align: top;
   white-space: nowrap;
-  width: calc(156px - 2px);
+  width: 154px;
+}
+
+.mv-tile {
+  background: rgb(250,250,250);
+}
+
+.mv-empty-tile {
+  background: rgb(245,245,245);
 }
 
 .mv-tile.hidden,
@@ -70,49 +75,42 @@
 }
 
 .mv-tile {
+  box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08);
   cursor: pointer;
   transition-duration: 200ms;
-  transition-property: transform, border, box-shadow, margin, opacity, width;
+  transition-property: transform, box-shadow, margin, opacity, width;
+  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
 }
 
-.mv-tile:focus:not(:hover) {
-  -webkit-filter: brightness(75%);
-  box-shadow: 0 1px 2px 0 rgba(0,0,0,0.1), 0 4px 8px 0 rgba(0,0,0,0.2);
+.mv-tile:hover:not(:active) {
+  box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.08);
+}
+
+.mv-tile:focus {
+  -webkit-filter: brightness(92%);
+}
+
+.mv-tile:active {
+  -webkit-filter: brightness(88%);
+  box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.12);
 }
 
 .mv-tile.blacklisted {
-  border: none !important;
   margin: 0;
   transform: scale(0, 0);
   width: 0;
 }
 
-.mv-tile:hover {
-  box-shadow: 0 1px 2px 0 rgba(0,0,0,0.1), 0 4px 8px 0 rgba(0,0,0,0.2);
-}
-
-.mv-tile.mv-blacklist {
-  margin-left: 0;
-  margin-right: 0;
-  opacity: 0;
-  transform: scale(0, 0);
-  transform-origin: 0 41px;
-  width: 0;
-}
-
 .mv-title {
-  -webkit-mask-image:
-    linear-gradient(to right, #000, #000, 100px, transparent);
-  border: none;
   height: 15px;
   left: 31px;
   line-height: 14px;
   overflow: hidden;
   padding: 0;
   position: absolute;
-  text-overflow: clip;
-  top: 8px;
-  width: calc(156px - 32px - 4px);
+  text-overflow: ellipsis;
+  top: 9px;
+  width: 120px;
 }
 
 .mv-title.multiline {
@@ -145,12 +143,11 @@
   border-radius: 0;
   cursor: pointer;
   display: block;
-  height: 94px;
-  left: 3px;
+  height: 96px;
   overflow: hidden;
   position: absolute;
-  top: 31px;
-  width: 148px;
+  top: 32px;
+  width: 154px;
 }
 
 .mv-thumb img {
@@ -160,15 +157,13 @@
 }
 
 .mv-thumb.failed-img {
-  background-color: #fff;
-  height: 94px;
-  width: 148px;
+  background-color: rgb(245,245,245);
 }
 
 /* We use ::after without content to provide an aditional element on top of the
  * thumbnail. */
 .mv-thumb.failed-img::after {
-  border: 8px solid #f2f2f2;
+  border: 8px solid rgb(215,215,215);
   border-radius: 50%;
   content: '';
   display: block;
@@ -178,7 +173,7 @@
 }
 
 .mv-x {
-  background: linear-gradient(to left, rgb(242,242,242) 60%, transparent);
+  background: linear-gradient(to left, rgb(250,250,250) 60%, transparent);
   border: none;
   cursor: pointer;
   height: 30px;
@@ -208,7 +203,7 @@
 }
 
 html[dir=rtl] .mv-x {
-  background: linear-gradient(to right, rgb(242,242,242) 60%, transparent);
+  background: linear-gradient(to right, rgb(250,250,250) 60%, transparent);
   left: -1px;
   right: auto;
 }
@@ -242,7 +237,7 @@
   margin: 0;
   pointer-events: none;
   position: absolute;
-  top: 7px;
+  top: 8px;
   width: 16px;
 }
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index f829663..212538c 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -204,16 +204,6 @@
 var updateTheme = function(info) {
   var themeStyle = [];
 
-  if (info.tileBorderColor) {
-    themeStyle.push(
-        '.mv-tile {' +
-        'border: 1px solid ' + info.tileBorderColor + '; }');
-  }
-  if (info.tileHoverBorderColor) {
-    themeStyle.push(
-        '.mv-tile:hover {' +
-        'border-color: ' + info.tileHoverBorderColor + '; }');
-  }
   if (info.isThemeDark) {
     themeStyle.push(
         '.mv-tile, .mv-empty-tile { ' +
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
index 3d43ba4..d1b5dda1 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
@@ -59,7 +59,8 @@
       <div class="start">[[title_]]</div>
       <template is="dom-if" if="[[showActionButton_]]">
         <div class="separator"></div>
-        <paper-button id="action-button" on-tap="proceed_">
+        <paper-button id="action-button" class="primary-button"
+            on-tap="proceed_">
           [[actionButtonLabel_]]
         </paper-button>
       </template>
diff --git a/chrome/browser/resources/snippets_internals.html b/chrome/browser/resources/snippets_internals.html
index c1f7511..0c2df4eb 100644
--- a/chrome/browser/resources/snippets_internals.html
+++ b/chrome/browser/resources/snippets_internals.html
@@ -175,6 +175,9 @@
                 <tr>
                   <td>Publisher name
                   <td jscontent="publisherName">
+                <tr>
+                  <td>Score
+                  <td jscontent="score">
               </table>
             </div>
       </table>
@@ -209,6 +212,9 @@
                 <tr>
                   <td>Publisher name
                   <td jscontent="publisherName">
+                <tr>
+                  <td>Score
+                  <td jscontent="score">
               </table>
             </div>
       </table>
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index 4296eb52..c38ea78 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -369,6 +369,8 @@
   if (![field_ currentEditor])
     return true;
   const NSRange all_range = NSMakeRange(0, GetTextLength());
+  if (all_range.length == 0)
+    return false;
   return NSEqualRanges(all_range, GetSelectedRange());
 }
 
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
index b3dcc322..06775bb 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
@@ -354,23 +354,8 @@
 }
 
 - (NSVisualEffectView*)visualEffectView {
-  // NSVisualEffectView is only available on OS X 10.10 and higher.
-  if (!base::mac::IsAtLeastOS10_10())
-    return nil;
-
-  NSView* rootView = [[self window] contentView];
-  if (!chrome::ShouldUseFullSizeContentView()) {
-    rootView = [rootView superview];
-  }
-
-  Class nsVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
-  DCHECK(nsVisualEffectViewClass);
-  for (NSView* view in [rootView subviews]) {
-    if ([view isKindOfClass:nsVisualEffectViewClass]) {
-      return base::mac::ObjCCast<NSVisualEffectView>(view);
-    }
-  }
-  return nil;
+  return [[BrowserWindowController
+      browserWindowControllerForWindow:[self window]] visualEffectView];
 }
 
 - (void)setController:(TabStripController*)controller {
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
index afa1e44..c088f4e1 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
@@ -51,6 +51,7 @@
   base::scoped_nsobject<FocusTracker> focusBeforeOverlay_;
   BOOL closeDeferred_;  // If YES, call performClose: in removeOverlay:.
 }
+@property(readonly, nonatomic) NSVisualEffectView* visualEffectView;
 @property(readonly, nonatomic) NSView* tabStripBackgroundView;
 @property(readonly, nonatomic) TabStripView* tabStripView;
 @property(readonly, nonatomic) FastResizeView* tabContentArea;
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
index f86fd8cf..368f4e5 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
@@ -161,6 +161,10 @@
   [super dealloc];
 }
 
+- (NSVisualEffectView*)visualEffectView {
+  return visualEffectView_;
+}
+
 - (NSView*)tabStripBackgroundView {
   return tabStripBackgroundView_;
 }
@@ -437,13 +441,20 @@
   if (hasTitleBar)
     return;
 
-  visualEffectView_.reset(
-      [[nsVisualEffectViewClass alloc]
-          initWithFrame:[tabStripBackgroundView_ frame]]);
+  // NSVisualEffectView provides hints about text anti-aliasing that are wrong
+  // when anything is drawn over it (like a tint or theme image). Wrapping it
+  // stops it from being used for hints. See https://crbug.com/593835.
+  NSView* visualEffectWrapperView = [[[NSView alloc]
+      initWithFrame:[tabStripBackgroundView_ frame]] autorelease];
+
+  visualEffectView_.reset([[nsVisualEffectViewClass alloc]
+      initWithFrame:visualEffectWrapperView.bounds]);
   DCHECK(visualEffectView_);
 
-  [visualEffectView_ setAutoresizingMask:
-      [tabStripBackgroundView_ autoresizingMask]];
+  [visualEffectWrapperView
+      setAutoresizingMask:[tabStripBackgroundView_ autoresizingMask]];
+  [visualEffectView_
+      setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
   [tabStripBackgroundView_
       setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
 
@@ -459,10 +470,12 @@
   [visualEffectView_ setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
   [visualEffectView_ setState:NSVisualEffectStateFollowsWindowActiveState];
 
+  [visualEffectWrapperView addSubview:visualEffectView_];
+
   if (chrome::ShouldUseFullSizeContentView()) {
-    [[window contentView] addSubview:visualEffectView_];
+    [[window contentView] addSubview:visualEffectWrapperView];
   } else {
-    [rootView addSubview:visualEffectView_
+    [rootView addSubview:visualEffectWrapperView
               positioned:NSWindowBelow
               relativeTo:nil];
   }
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 6feaf29..145bb78 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -855,7 +855,7 @@
 
   // Delete the content
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DELETE, 0));
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
   omnibox_view->GetSelectionBounds(&start, &end);
   EXPECT_EQ(0U, start);
   EXPECT_EQ(0U, end);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index d50af54b..08d0278 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -435,7 +435,7 @@
 
 bool OmniboxViewViews::IsSelectAll() const {
   // TODO(oshima): IME support.
-  return text() == GetSelectedText();
+  return !text().empty() && text() == GetSelectedText();
 }
 
 bool OmniboxViewViews::DeleteAtEndPressed() {
diff --git a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
index 35a183d..46f4b8c 100644
--- a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
+++ b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
@@ -120,7 +120,7 @@
   controller_->RemoveObserver(this);
 
   CallJavascriptFunction("cr.webUIListenerCallback",
-                         base::Value("chrome-cleanup-dismiss"));
+                         base::Value("chrome-cleanup-on-dismiss"));
 }
 
 void ChromeCleanupHandler::HandleRegisterChromeCleanerObserver(
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index 51dab6e3..db8a5ae 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -111,7 +111,7 @@
 bool HasIntersection(const std::vector<std::string>& a,
                      const std::set<std::string>& b) {
   for (const std::string& item : a) {
-    if (base::ContainsValue(b, item)) {
+    if (b.count(item)) {
       return true;
     }
   }
@@ -124,7 +124,7 @@
   base::EraseIf(
       *suggestions,
       [&ids_lookup](const std::unique_ptr<RemoteSuggestion>& suggestion) {
-        return base::ContainsValue(ids_lookup, suggestion->id());
+        return ids_lookup.count(suggestion->id());
       });
 }
 
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 5960a93b..773dac1d 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -118,6 +118,8 @@
     "password_ui_utils.h",
     "psl_matching_helper.cc",
     "psl_matching_helper.h",
+    "site_affiliation/asset_link_retriever.cc",
+    "site_affiliation/asset_link_retriever.h",
     "sql_table_builder.cc",
     "sql_table_builder.h",
     "statistics_table.cc",
@@ -318,6 +320,7 @@
     "password_syncable_service_unittest.cc",
     "password_ui_utils_unittest.cc",
     "psl_matching_helper_unittest.cc",
+    "site_affiliation/asset_link_retriever_unittest.cc",
     "sql_table_builder_unittest.cc",
     "statistics_table_unittest.cc",
     "suppressed_form_fetcher_unittest.cc",
diff --git a/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc b/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc
new file mode 100644
index 0000000..dabc0e4c
--- /dev/null
+++ b/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc
@@ -0,0 +1,75 @@
+// 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 "components/password_manager/core/browser/site_affiliation/asset_link_retriever.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/url_fetcher.h"
+
+namespace password_manager {
+
+AssetLinkRetriever::AssetLinkRetriever(GURL file_url)
+    : url_(std::move(file_url)), state_(State::INACTIVE), error_(false) {
+  DCHECK(url_.is_valid());
+  DCHECK(url_.SchemeIs(url::kHttpsScheme));
+}
+
+void AssetLinkRetriever::Start(net::URLRequestContextGetter* context_getter) {
+  if (state_ != State::INACTIVE)
+    return;
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("asset_links", R"(
+        semantics {
+          sender: "Asset Links Fetcher"
+          description:
+            "The asset links is a JSON file hosted on "
+            "https://<domain>/.well-known/assetlinks.json. It contains "
+            "different permissions the site gives to apps/other sites. Chrome "
+            "looks for a permission to delegate the credentials from the site "
+            "to another domain. It's used for handling the stored credentials "
+            "in the password manager."
+          trigger:
+            "Load a site where it's possible to sign-in. The site can have a "
+            "password form or use the Credential Management API."
+          data: "None."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: false
+          setting: "No setting"
+          policy_exception_justification:
+            "The file is considered to be a resource of the page loaded."
+        })");
+  fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this,
+                                     traffic_annotation);
+  fetcher_->SetRequestContext(context_getter);
+  fetcher_->SetLoadFlags(
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
+      net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_MAYBE_USER_GESTURE);
+  fetcher_->SetStopOnRedirect(true);
+  fetcher_->Start();
+  state_ = State::NETWORK_REQUEST;
+}
+
+AssetLinkRetriever::~AssetLinkRetriever() = default;
+
+void AssetLinkRetriever::OnURLFetchComplete(const net::URLFetcher* source) {
+  DCHECK(source == fetcher_.get());
+
+  error_ = !source->GetStatus().is_success() ||
+           source->GetResponseCode() != net::HTTP_OK;
+  if (error_) {
+    state_ = State::FINISHED;
+  } else {
+    // TODO(crbug.com/630555): Start parsing here.
+  }
+  fetcher_.reset();
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h b/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h
new file mode 100644
index 0000000..42ae1a1
--- /dev/null
+++ b/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h
@@ -0,0 +1,67 @@
+// 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace password_manager {
+
+// The class is responsible for fetching and parsing the digit asset links file.
+// The file is a JSON containing different directives for the domain. The class
+// is only interested in those related to credentials delegations.
+// The spec is
+// https://github.com/google/digitalassetlinks/blob/master/well-known/details.md
+class AssetLinkRetriever : public base::RefCounted<AssetLinkRetriever>,
+                           public net::URLFetcherDelegate {
+ public:
+  enum class State {
+    INACTIVE,
+    NETWORK_REQUEST,
+    FINISHED,
+  };
+
+  explicit AssetLinkRetriever(GURL file_url);
+
+  // Starts a network request if the current state is INACTIVE. All the calls
+  // afterwards are ignored.
+  void Start(net::URLRequestContextGetter* context_getter);
+
+  State state() const { return state_; }
+
+  bool error() const { return error_; }
+
+ private:
+  friend class base::RefCounted<AssetLinkRetriever>;
+  ~AssetLinkRetriever() override;
+
+  // net::URLFetcherDelegate:
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  // URL of the file retrieved.
+  const GURL url_;
+
+  // Current state of the retrieval.
+  State state_;
+
+  // Whether the reading finished with error.
+  bool error_;
+
+  std::unique_ptr<net::URLFetcher> fetcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssetLinkRetriever);
+};
+
+}  // namespace password_manager
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
diff --git a/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc b/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc
new file mode 100644
index 0000000..700dbec5
--- /dev/null
+++ b/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc
@@ -0,0 +1,108 @@
+// 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 "components/password_manager/core/browser/site_affiliation/asset_link_retriever.h"
+
+#include <memory>
+
+#include "base/test/scoped_task_environment.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+constexpr char kAssetLinkFile[] =
+    "https://example.com/.well-known/assetlinks.json";
+
+// A test URL fecther which is very cautious about not following the redirects.
+class AssetLinksTestFetcher : public net::FakeURLFetcher {
+ public:
+  using FakeURLFetcher::FakeURLFetcher;
+
+  ~AssetLinksTestFetcher() override { EXPECT_TRUE(stop_on_redirect_); }
+
+  // FakeURLFetcher:
+  void SetStopOnRedirect(bool stop_on_redirect) override {
+    FakeURLFetcher::SetStopOnRedirect(stop_on_redirect);
+    stop_on_redirect_ = stop_on_redirect;
+  }
+
+ private:
+  bool stop_on_redirect_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(AssetLinksTestFetcher);
+};
+
+std::unique_ptr<net::FakeURLFetcher> AssetLinksFetcherCreator(
+    const GURL& url,
+    net::URLFetcherDelegate* delegate,
+    const std::string& response_data,
+    net::HttpStatusCode response_code,
+    net::URLRequestStatus::Status status) {
+  return base::MakeUnique<AssetLinksTestFetcher>(url, delegate, response_data,
+                                                 response_code, status);
+}
+
+class AssetLinkRetrieverTest : public testing::Test {
+ public:
+  AssetLinkRetrieverTest();
+
+  net::TestURLRequestContextGetter* request_context() const {
+    return request_context_.get();
+  }
+
+  net::FakeURLFetcherFactory& factory() { return factory_; }
+
+  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  net::FakeURLFetcherFactory factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssetLinkRetrieverTest);
+};
+
+AssetLinkRetrieverTest::AssetLinkRetrieverTest()
+    : request_context_(new net::TestURLRequestContextGetter(
+          base::ThreadTaskRunnerHandle::Get())),
+      factory_(nullptr, base::Bind(&AssetLinksFetcherCreator)) {}
+
+// Load the asset links resource that isn't available.
+TEST_F(AssetLinkRetrieverTest, LoadNonExistent) {
+  scoped_refptr<AssetLinkRetriever> asset_link_retriever =
+      base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
+  EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
+
+  factory().SetFakeResponse(GURL(kAssetLinkFile), std::string(),
+                            net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED);
+  asset_link_retriever->Start(request_context());
+  EXPECT_EQ(AssetLinkRetriever::State::NETWORK_REQUEST,
+            asset_link_retriever->state());
+
+  RunUntilIdle();
+  EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
+  EXPECT_TRUE(asset_link_retriever->error());
+}
+
+// Load the asset links resource that replies with redirect. It should be
+// treated as an error.
+TEST_F(AssetLinkRetrieverTest, LoadRedirect) {
+  scoped_refptr<AssetLinkRetriever> asset_link_retriever =
+      base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
+  EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
+
+  factory().SetFakeResponse(GURL(kAssetLinkFile), std::string(),
+                            net::HTTP_FOUND, net::URLRequestStatus::CANCELED);
+  asset_link_retriever->Start(request_context());
+
+  RunUntilIdle();
+  EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
+  EXPECT_TRUE(asset_link_retriever->error());
+}
+
+}  // namespace
+}  // namespace password_manager
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index fce37fe..723e96c1 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1278,7 +1278,9 @@
 
       If you enable or disable this setting, users cannot change or override the "Enable phishing and malware protection" setting in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>.
 
-      If this policy is left not set, this will be enabled but the user will be able to change it.''',
+      If this policy is left not set, this will be enabled but the user will be able to change it.
+
+      See https://developers.google.com/safe-browsing for  more info on SafeBrowsing.''',
     },
     {
       'name': 'MetricsReportingEnabled',
@@ -5909,7 +5911,9 @@
       'tags': [],
       'desc': '''The Safe Browsing service shows a warning page when users navigate to sites that are flagged as potentially malicious. Enabling this setting prevents users from proceeding anyway from the warning page to the malicious site.
 
-      If this setting is disabled or not configured then users can choose to proceed to the flagged site after being shown the warning.''',
+      If this setting is disabled or not configured then users can choose to proceed to the flagged site after being shown the warning.
+
+      See https://developers.google.com/safe-browsing for  more info on SafeBrowsing.''',
     },
     {
       'name': 'SafeBrowsingExtendedReportingOptInAllowed',
@@ -5924,7 +5928,9 @@
       'id': 299,
       'caption': '''Allow users to opt in to Safe Browsing extended reporting''',
       'tags': [],
-      'desc': '''Setting this policy to false stops users from choosing to send some system information and page content to Google servers. If this setting is true or not configured, then users will be allowed to send some system information and page content to Safe Browsing to help detect dangerous apps and sites.''',
+      'desc': '''Setting this policy to false stops users from choosing to send some system information and page content to Google servers. If this setting is true or not configured, then users will be allowed to send some system information and page content to Safe Browsing to help detect dangerous apps and sites.
+
+      See https://developers.google.com/safe-browsing for  more info on SafeBrowsing.''',
     },
     {
       'name': 'SpellCheckServiceEnabled',
@@ -9314,7 +9320,8 @@
       'desc': '''Enables component updates for all components in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> when not set or set to True.
 
       If set to False, updates to components are disabled. However, some components are exempt from this policy: updates to any component that does not contain executable code, or does not significantly alter the behavior of the browser, or is critical for its security will not be disabled.
-      Examples of such components include the certificate revocation lists and safe browsing data. ''',
+      Examples of such components include the certificate revocation lists and safe browsing data.
+      See https://developers.google.com/safe-browsing for  more info on SafeBrowsing.''',
     },
     {
       'name': 'NativePrinters',
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index b2aad33..83e64632 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -66,7 +66,7 @@
   std::unique_ptr<AppCacheHost> TransferHostOut(int host_id);
   void TransferHostIn(int new_host_id, std::unique_ptr<AppCacheHost> host);
 
-  // PlzNaviate
+  // PlzNavigate
   // The AppCacheHost is precreated by the AppCacheNavigationHandleCore class
   // when a navigation is initiated. We register the host with the backend in
   // this function and ignore registrations for this host id from the renderer.
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 2e16dfd..272e0cd 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <algorithm>
 #include <string>
 #include <utility>
 #include <vector>
@@ -85,6 +86,7 @@
 #include "content/browser/webui/url_data_manager.h"
 #include "content/common/content_switches_internal.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
+#include "content/common/task_scheduler.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/gpu_data_manager_observer.h"
@@ -940,6 +942,23 @@
     if (!task_scheduler_init_params)
       task_scheduler_init_params = GetDefaultTaskSchedulerInitParams();
     DCHECK(task_scheduler_init_params);
+
+    // If a renderer lives in the browser process, adjust the number of threads
+    // in the foreground pool.
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kSingleProcess)) {
+      const base::SchedulerWorkerPoolParams&
+          current_foreground_worker_pool_params(
+              task_scheduler_init_params->foreground_worker_pool_params);
+      task_scheduler_init_params->foreground_worker_pool_params =
+          base::SchedulerWorkerPoolParams(
+              current_foreground_worker_pool_params.standby_thread_policy(),
+              std::max(GetMinThreadsInRendererTaskSchedulerForegroundPool(),
+                       current_foreground_worker_pool_params.max_threads()),
+              current_foreground_worker_pool_params.suggested_reclaim_time(),
+              current_foreground_worker_pool_params.backward_compatibility());
+    }
+
     base::TaskScheduler::GetInstance()->Start(
         *task_scheduler_init_params.get());
   }
@@ -1248,7 +1267,8 @@
           TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread");
           // Clean up state that lives on or uses the FILE thread before it goes
           // away.
-          save_file_manager_->Shutdown();
+          if (save_file_manager_)
+            save_file_manager_->Shutdown();
           ResetThread_FILE();
           break;
         }
@@ -1325,7 +1345,7 @@
   }
   {
     TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:AudioMan");
-    if (!audio_manager_->Shutdown()) {
+    if (audio_manager_ && !audio_manager_->Shutdown()) {
       // Intentionally leak AudioManager if shutdown failed.
       // We might run into various CHECK(s) in AudioManager destructor.
       ignore_result(audio_manager_.release());
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index ae3789e..b50b639 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/timer/timer.h"
@@ -190,6 +191,8 @@
 #endif
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(BrowserMainLoopTest, CreateThreadsInSingleProcess);
+
   void InitializeMainThread();
 
   // Called just before creating the threads
diff --git a/content/browser/browser_main_loop_unittest.cc b/content/browser/browser_main_loop_unittest.cc
new file mode 100644
index 0000000..3e2f04f0
--- /dev/null
+++ b/content/browser/browser_main_loop_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_main_loop.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/test/scoped_command_line.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/main_function_params.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+// Verify that a single-process browser process has at least as many threads as
+// the number of cores in its foreground pool.
+TEST(BrowserMainLoopTest, CreateThreadsInSingleProcess) {
+  {
+    base::MessageLoop message_loop;
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitch(
+        switches::kSingleProcess);
+    MainFunctionParams main_function_params(
+        *scoped_command_line.GetProcessCommandLine());
+    BrowserMainLoop browser_main_loop(main_function_params);
+    browser_main_loop.MainMessageLoopStart();
+    browser_main_loop.CreateThreads();
+    EXPECT_GE(base::TaskScheduler::GetInstance()
+                  ->GetMaxConcurrentTasksWithTraitsDeprecated(
+                      {base::TaskPriority::USER_VISIBLE}),
+              base::SysInfo::NumberOfProcessors());
+    browser_main_loop.ShutdownThreadsAndCleanUp();
+  }
+  base::TaskScheduler::GetInstance()->JoinForTesting();
+  base::TaskScheduler::SetInstance(nullptr);
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/data_url_navigation_browsertest.cc b/content/browser/frame_host/data_url_navigation_browsertest.cc
index 2c62b1d..7d48a946e 100644
--- a/content/browser/frame_host/data_url_navigation_browsertest.cc
+++ b/content/browser/frame_host/data_url_navigation_browsertest.cc
@@ -649,10 +649,18 @@
   NavigateAndCheckDownload(GURL("data:application/octet-stream,test"));
 }
 
+#if defined(OS_ANDROID)
+// Flaky on android: https://crbug.com/734563
+#define MAYBE_OctetStream_WindowOpen_Download \
+  DISABLED_OctetStream_WindowOpen_Download
+#else
+#define MAYBE_OctetStream_WindowOpen_Download OctetStream_WindowOpen_Download
+#endif
+
 // Test that window.open to a data URL results in a download if the URL has a
 // binary mime type.
 IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
-                       OctetStream_WindowOpen_Download) {
+                       MAYBE_OctetStream_WindowOpen_Download) {
   NavigateToURL(shell(),
                 embedded_test_server()->GetURL("/data_url_navigations.html"));
   ExecuteScriptAndCheckWindowOpenDownload(
@@ -705,10 +713,19 @@
   NavigateAndCheckDownload(GURL("data:unknown/mimetype,test"));
 }
 
+#if defined(OS_ANDROID)
+// Flaky on android: https://crbug.com/734563
+#define MAYBE_UnknownMimeType_WindowOpen_Download \
+  DISABLED_UnknownMimeType_WindowOpen_Download
+#else
+#define MAYBE_UnknownMimeType_WindowOpen_Download \
+  UnknownMimeType_WindowOpen_Download
+#endif
+
 // Test that window.open to a data URL results in a download if the URL has an
 // unknown mime type.
 IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
-                       UnknownMimeType_WindowOpen_Download) {
+                       MAYBE_UnknownMimeType_WindowOpen_Download) {
   NavigateToURL(shell(),
                 embedded_test_server()->GetURL("/data_url_navigations.html"));
   ExecuteScriptAndCheckWindowOpenDownload(
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index a93fd313..559f45b 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -88,6 +88,8 @@
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMediaPaused, OnMediaPaused)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMediaPlaying,
                         OnMediaPlaying)
+    IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMutedStatusChanged,
+                        OnMediaMutedStatusChanged)
     IPC_MESSAGE_HANDLER(
         MediaPlayerDelegateHostMsg_OnMediaEffectivelyFullscreenChanged,
         OnMediaEffectivelyFullscreenChanged)
@@ -299,6 +301,14 @@
     CancelVideoLock();
 }
 
+void MediaWebContentsObserver::OnMediaMutedStatusChanged(
+    RenderFrameHost* render_frame_host,
+    int delegate_id,
+    bool muted) {
+  const MediaPlayerId id(render_frame_host, delegate_id);
+  web_contents_impl()->MediaMutedStatusChanged(id, muted);
+}
+
 void MediaWebContentsObserver::AddMediaPlayerEntry(
     const MediaPlayerId& id,
     ActiveMediaPlayerMap* player_map) {
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h
index 095cddf..c60eaac 100644
--- a/content/browser/media/media_web_contents_observer.h
+++ b/content/browser/media/media_web_contents_observer.h
@@ -94,6 +94,9 @@
   void OnMediaSizeChanged(RenderFrameHost* render_frame_host,
                           int delegate_id,
                           const gfx::Size& size);
+  void OnMediaMutedStatusChanged(RenderFrameHost* render_frame_host,
+                                 int delegate_id,
+                                 bool muted);
 
   // Clear |render_frame_host|'s tracking entry for its WakeLocks.
   void ClearWakeLocks(RenderFrameHost* render_frame_host);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 928e5369..d1bd434 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5747,4 +5747,11 @@
   }
 }
 
+void WebContentsImpl::MediaMutedStatusChanged(
+    const WebContentsObserver::MediaPlayerId& id,
+    bool muted) {
+  for (auto& observer : observers_)
+    observer.MediaMutedStatusChanged(id, muted);
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d683bb9..4ef2a60d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -801,6 +801,9 @@
   // No interstitial page should already be attached.
   void AttachInterstitialPage(InterstitialPageImpl* interstitial_page) override;
 
+  void MediaMutedStatusChanged(const WebContentsObserver::MediaPlayerId& id,
+                               bool muted);
+
   // Unsets the currently showing interstitial.
   void DetachInterstitialPage() override;
 
diff --git a/content/browser/webrtc/webrtc_datachannel_browsertest.cc b/content/browser/webrtc/webrtc_datachannel_browsertest.cc
index 56062302..53ec198 100644
--- a/content/browser/webrtc/webrtc_datachannel_browsertest.cc
+++ b/content/browser/webrtc/webrtc_datachannel_browsertest.cc
@@ -32,7 +32,8 @@
   DISALLOW_COPY_AND_ASSIGN(WebRtcDataChannelTest);
 };
 
-IN_PROC_BROWSER_TEST_F(WebRtcDataChannelTest, DataChannelGC) {
+// Flaky on all platforms: https://crbug.com/734567
+IN_PROC_BROWSER_TEST_F(WebRtcDataChannelTest, DISABLED_DataChannelGC) {
   MakeTypicalCall("testDataChannelGC();", kDataChannelHtmlFile);
 }
 
diff --git a/content/browser/webrtc/webrtc_depth_capture_browsertest.cc b/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
index 753bd46..97a8e5e 100644
--- a/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
@@ -105,8 +105,17 @@
       "%s({video: true});", kGetDepthStreamAndCameraCalibration));
 }
 
+#if defined(OS_ANDROID)
+// Flaky on android: https://crbug.com/734558
+#define MAYBE_GetBothStreamsAndCheckForFeaturesPresence \
+  DISABLED_GetBothStreamsAndCheckForFeaturesPresence
+#else
+#define MAYBE_GetBothStreamsAndCheckForFeaturesPresence \
+  GetBothStreamsAndCheckForFeaturesPresence
+#endif
+
 IN_PROC_BROWSER_TEST_F(WebRtcTwoDeviceDepthCaptureBrowserTest,
-                       GetBothStreamsAndCheckForFeaturesPresence) {
+                       MAYBE_GetBothStreamsAndCheckForFeaturesPresence) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   command_line->AppendSwitchASCII("--enable-blink-features",
                                   "MediaGetSettings,MediaCaptureDepth");
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 687c325..0cd11af 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -23,6 +23,7 @@
 #if defined(OS_WIN)
 // These tests are flaky on WebRTC Windows bots: https://crbug.com/633242.
 #define MAYBE_GetPhotoCapabilities DISABLED_GetPhotoCapabilities
+#define MAYBE_GetPhotoSettings DISABLED_GetPhotoSettings
 #define MAYBE_TakePhoto DISABLED_TakePhoto
 #define MAYBE_GrabFrame DISABLED_GrabFrame
 #define MAYBE_GetTrackCapabilities DISABLED_GetTrackCapabilities
@@ -30,6 +31,7 @@
 #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
 #else
 #define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
+#define MAYBE_GetPhotoSettings GetPhotoSettings
 #define MAYBE_TakePhoto TakePhoto
 #define MAYBE_GrabFrame GrabFrame
 #define MAYBE_GetTrackCapabilities GetTrackCapabilities
@@ -122,6 +124,10 @@
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetPhotoCapabilities()"));
 }
 
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_GetPhotoSettings) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetPhotoSettings()"));
+}
 IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_TakePhoto) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhoto()"));
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 738e06d..22a23ad 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -340,6 +340,8 @@
     "speech_recognition_messages.h",
     "swapped_out_messages.cc",
     "swapped_out_messages.h",
+    "task_scheduler.cc",
+    "task_scheduler.h",
     "text_input_client_messages.h",
     "text_input_state.cc",
     "text_input_state.h",
diff --git a/content/common/content_security_policy/csp_context.cc b/content/common/content_security_policy/csp_context.cc
index 7d3aebef..4fd6b8a 100644
--- a/content/common/content_security_policy/csp_context.cc
+++ b/content/common/content_security_policy/csp_context.cc
@@ -26,8 +26,7 @@
 
 }  // namespace
 
-CSPContext::CSPContext() : has_self_(false) {}
-
+CSPContext::CSPContext() {}
 CSPContext::~CSPContext() {}
 
 bool CSPContext::IsAllowedByCsp(CSPDirective::Name directive_name,
@@ -69,40 +68,24 @@
 }
 
 void CSPContext::SetSelf(const url::Origin origin) {
-  if (origin.unique()) {
-    // TODO(arthursonzogni): Decide what to do with unique origins.
-    has_self_ = false;
+  self_source_.reset();
+
+  // When the origin is unique, no URL should match with 'self'. That's why
+  // |self_source_| stays undefined here.
+  if (origin.unique())
     return;
-  }
 
   if (origin.scheme() == url::kFileScheme) {
-    has_self_ = true;
-    self_scheme_ = url::kFileScheme;
     self_source_ = CSPSource(url::kFileScheme, "", false, url::PORT_UNSPECIFIED,
                              false, "");
     return;
   }
 
-  has_self_ = true;
-  self_scheme_ = origin.scheme();
   self_source_ = CSPSource(
       origin.scheme(), origin.host(), false,
-      origin.port() == 0 ? url::PORT_UNSPECIFIED : origin.port(),  // port
-      false, "");
-}
+      origin.port() == 0 ? url::PORT_UNSPECIFIED : origin.port(), false, "");
 
-bool CSPContext::AllowSelf(const GURL& url) {
-  return has_self_ && CSPSource::Allow(self_source_, url, this);
-}
-
-bool CSPContext::ProtocolIsSelf(const GURL& url) {
-  if (!has_self_)
-    return false;
-  return url.SchemeIs(self_scheme_);
-}
-
-const std::string& CSPContext::GetSelfScheme() {
-  return self_scheme_;
+  DCHECK_NE("", self_source_->scheme);
 }
 
 bool CSPContext::SchemeShouldBypassCSP(const base::StringPiece& scheme) {
@@ -117,12 +100,6 @@
   return;
 }
 
-bool CSPContext::SelfSchemeShouldBypassCsp() {
-  if (!has_self_)
-    return false;
-  return SchemeShouldBypassCSP(self_scheme_);
-}
-
 void CSPContext::ReportContentSecurityPolicyViolation(
     const CSPViolationParams& violation_params) {
   return;
diff --git a/content/common/content_security_policy/csp_context.h b/content/common/content_security_policy/csp_context.h
index 51471f43..78b7a090 100644
--- a/content/common/content_security_policy/csp_context.h
+++ b/content/common/content_security_policy/csp_context.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/optional.h"
 #include "content/common/content_export.h"
 #include "content/common/content_security_policy/content_security_policy.h"
 #include "content/common/content_security_policy_header.h"
@@ -59,15 +60,18 @@
                                     GURL* new_url);
 
   void SetSelf(const url::Origin origin);
-  bool AllowSelf(const GURL& url);
-  bool ProtocolIsSelf(const GURL& url);
-  const std::string& GetSelfScheme();
+
+  // When a CSPSourceList contains 'self', the url is allowed when it match the
+  // CSPSource returned by this function.
+  // Sometimes there is no 'self' source. It means that the current origin is
+  // unique and no urls will match 'self' whatever they are.
+  // Note: When there is a 'self' source, its scheme is guaranteed to be
+  // non-empty.
+  const base::Optional<CSPSource>& self_source() { return self_source_; }
 
   virtual void ReportContentSecurityPolicyViolation(
       const CSPViolationParams& violation_params);
 
-  bool SelfSchemeShouldBypassCsp();
-
   void ResetContentSecurityPolicies() { policies_.clear(); }
   void AddContentSecurityPolicy(const ContentSecurityPolicy& policy) {
     policies_.push_back(policy);
@@ -90,10 +94,7 @@
       SourceLocation* source_location) const;
 
  private:
-  bool has_self_ = false;
-  std::string self_scheme_;
-  CSPSource self_source_;
-
+  base::Optional<CSPSource> self_source_;
   std::vector<ContentSecurityPolicy> policies_;
 
   DISALLOW_COPY_AND_ASSIGN(CSPContext);
diff --git a/content/common/content_security_policy/csp_source.cc b/content/common/content_security_policy/csp_source.cc
index 7ebd7e75..6e1deee 100644
--- a/content/common/content_security_policy/csp_source.cc
+++ b/content/common/content_security_policy/csp_source.cc
@@ -37,28 +37,29 @@
 SchemeMatchingResult SourceAllowScheme(const CSPSource& source,
                                        const GURL& url,
                                        CSPContext* context) {
-  const std::string& source_scheme =
-      source.scheme.empty() ? context->GetSelfScheme() : source.scheme;
-
-  if (source_scheme.empty()) {
-    if (context->ProtocolIsSelf(url))
-      return SchemeMatchingResult::MatchingExact;
+  // The source doesn't specify a scheme and the current origin is unique. In
+  // this case, the url doesn't match regardless of its scheme.
+  if (source.scheme.empty() && !context->self_source())
     return SchemeMatchingResult::NotMatching;
-  }
 
-  if (url.SchemeIs(source_scheme))
+  // |allowed_scheme| is guaranteed to be non-empty.
+  const std::string& allowed_scheme =
+      source.scheme.empty() ? context->self_source()->scheme : source.scheme;
+
+  if (url.SchemeIs(allowed_scheme))
     return SchemeMatchingResult::MatchingExact;
 
-  if ((source_scheme == url::kHttpScheme && url.SchemeIs(url::kHttpsScheme)) ||
-      (source_scheme == url::kHttpScheme &&
+  // Implicitly allow using a more secure version of a protocol when the
+  // non-secure one is allowed.
+  if ((allowed_scheme == url::kHttpScheme && url.SchemeIs(url::kHttpsScheme)) ||
+      (allowed_scheme == url::kHttpScheme &&
        url.SchemeIs(url::kHttpsSuboriginScheme)) ||
-      (source_scheme == url::kWsScheme && url.SchemeIs(url::kWssScheme))) {
+      (allowed_scheme == url::kWsScheme && url.SchemeIs(url::kWssScheme))) {
     return SchemeMatchingResult::MatchingUpgrade;
   }
-
-  if ((source_scheme == url::kHttpScheme &&
+  if ((allowed_scheme == url::kHttpScheme &&
        url.SchemeIs(url::kHttpSuboriginScheme)) ||
-      (source_scheme == url::kHttpsScheme &&
+      (allowed_scheme == url::kHttpsScheme &&
        url.SchemeIs(url::kHttpsSuboriginScheme))) {
     return SchemeMatchingResult::MatchingExact;
   }
diff --git a/content/common/content_security_policy/csp_source_list.cc b/content/common/content_security_policy/csp_source_list.cc
index be6ba9c4..d48b7c77 100644
--- a/content/common/content_security_policy/csp_source_list.cc
+++ b/content/common/content_security_policy/csp_source_list.cc
@@ -27,7 +27,10 @@
 CSPSourceList::CSPSourceList(bool allow_self,
                              bool allow_star,
                              std::vector<CSPSource> sources)
-    : allow_self(allow_self), allow_star(allow_star), sources(sources) {}
+    : allow_self(allow_self), allow_star(allow_star), sources(sources) {
+  // When the '*' source is used, it must be the only one.
+  DCHECK(!allow_star || (!allow_self && sources.empty()));
+}
 
 CSPSourceList::CSPSourceList(const CSPSourceList&) = default;
 CSPSourceList::~CSPSourceList() = default;
@@ -44,14 +47,18 @@
   // list.
   if (source_list.allow_star) {
     if (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsSuborigin() ||
-        url.SchemeIsWSOrWSS() || url.SchemeIs("ftp") ||
-        context->ProtocolIsSelf(url))
+        url.SchemeIsWSOrWSS() || url.SchemeIs("ftp")) {
       return true;
-
-    return AllowFromSources(url, source_list.sources, context, is_redirect);
+    }
+    if (context->self_source() && url.SchemeIs(context->self_source()->scheme))
+      return true;
   }
 
-  if (source_list.allow_self && context->AllowSelf(url)) return true;
+  if (source_list.allow_self && context->self_source() &&
+      CSPSource::Allow(context->self_source().value(), url, context,
+                       is_redirect)) {
+    return true;
+  }
 
   return AllowFromSources(url, source_list.sources, context, is_redirect);
 }
diff --git a/content/common/content_security_policy/csp_source_list_unittest.cc b/content/common/content_security_policy/csp_source_list_unittest.cc
index 43a5cad..e675263 100644
--- a/content/common/content_security_policy/csp_source_list_unittest.cc
+++ b/content/common/content_security_policy/csp_source_list_unittest.cc
@@ -92,4 +92,20 @@
   EXPECT_FALSE(Allow(source_list, GURL("https://example.test/"), &context));
 }
 
+TEST(CSPSourceTest, SelfIsUnique) {
+  // Policy: 'self'
+  CSPSourceList source_list(true,                       // allow_self
+                            false,                      // allow_star:
+                            std::vector<CSPSource>());  // source_list
+  CSPContext context;
+
+  context.SetSelf(url::Origin(GURL("http://a.com")));
+  EXPECT_TRUE(Allow(source_list, GURL("http://a.com"), &context));
+  EXPECT_FALSE(Allow(source_list, GURL("data:text/html,hello"), &context));
+
+  context.SetSelf(url::Origin(GURL("data:text/html,<iframe src=[...]>")));
+  EXPECT_FALSE(Allow(source_list, GURL("http://a.com"), &context));
+  EXPECT_FALSE(Allow(source_list, GURL("data:text/html,hello"), &context));
+}
+
 }  // namespace content
diff --git a/content/common/content_security_policy/csp_source_unittest.cc b/content/common/content_security_policy/csp_source_unittest.cc
index 50879f2..81a1c5f 100644
--- a/content/common/content_security_policy/csp_source_unittest.cc
+++ b/content/common/content_security_policy/csp_source_unittest.cc
@@ -112,14 +112,15 @@
     EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
     EXPECT_TRUE(Allow(source, GURL("ftp://a.com"), &context));
 
-    // Self's scheme is unique.
+    // Self's scheme is unique (non standard scheme).
     context.SetSelf(url::Origin(GURL("non-standard-scheme://a.com")));
-    // TODO(mkwst, arthursonzogni): This result might be wrong.
-    // See http://crbug.com/692449
     EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
-    // TODO(mkwst, arthursonzogni): This result might be wrong.
-    // See http://crbug.com/692449
     EXPECT_FALSE(Allow(source, GURL("non-standard-scheme://a.com"), &context));
+
+    // Self's scheme is unique (data-url).
+    context.SetSelf(url::Origin(GURL("data:text/html,<iframe src=[...]>")));
+    EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
+    EXPECT_FALSE(Allow(source, GURL("data:text/html,hello"), &context));
   }
 }
 
diff --git a/content/common/media/media_player_delegate_messages.h b/content/common/media/media_player_delegate_messages.h
index 9f6e0eb4..45b495b1 100644
--- a/content/common/media/media_player_delegate_messages.h
+++ b/content/common/media/media_player_delegate_messages.h
@@ -57,6 +57,10 @@
                     bool /* is_remote */,
                     media::MediaContentType /* media_content_type */)
 
+IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnMutedStatusChanged,
+                    int /* delegate_id, distinguishes instances */,
+                    bool /* the new muted status */)
+
 IPC_MESSAGE_ROUTED2(
     MediaPlayerDelegateHostMsg_OnMediaEffectivelyFullscreenChanged,
     int /* delegate_id, distinguishes instances */,
diff --git a/content/common/task_scheduler.cc b/content/common/task_scheduler.cc
new file mode 100644
index 0000000..fc1c086
--- /dev/null
+++ b/content/common/task_scheduler.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/task_scheduler.h"
+
+#include "base/sys_info.h"
+
+namespace content {
+
+int GetMinThreadsInRendererTaskSchedulerForegroundPool() {
+  return base::SysInfo::NumberOfProcessors();
+}
+
+}  // namespace content
diff --git a/content/common/task_scheduler.h b/content/common/task_scheduler.h
new file mode 100644
index 0000000..caacee36
--- /dev/null
+++ b/content/common/task_scheduler.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 CONTENT_COMMON_TASK_SCHEDULER_
+#define CONTENT_COMMON_TASK_SCHEDULER_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Returns the minimum number of threads that the TaskScheduler foreground pool
+// must have in a process that runs a renderer.
+int GetMinThreadsInRendererTaskSchedulerForegroundPool();
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_TASK_SCHEDULER_
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index a9ebcc33..798ceab 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -419,6 +419,7 @@
   virtual void MediaStoppedPlaying(const MediaPlayerInfo& video_type,
                                    const MediaPlayerId& id) {}
   virtual void MediaResized(const gfx::Size& size, const MediaPlayerId& id) {}
+  virtual void MediaMutedStatusChanged(const MediaPlayerId& id, bool muted) {}
 
   // Invoked when the renderer process changes the page scale factor.
   virtual void OnPageScaleFactorChanged(float page_scale_factor) {}
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 3da9f2ac..94359431 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -603,6 +603,8 @@
       "media/media_stream_center.h",
       "media/media_stream_constraints_util.cc",
       "media/media_stream_constraints_util.h",
+      "media/media_stream_constraints_util_audio.cc",
+      "media/media_stream_constraints_util_audio.h",
       "media/media_stream_constraints_util_sets.cc",
       "media/media_stream_constraints_util_sets.h",
       "media/media_stream_constraints_util_video_content.cc",
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc
index eafc8ec..401350e 100644
--- a/content/renderer/media/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/media_stream_audio_processor_options.cc
@@ -275,6 +275,17 @@
   return "";
 }
 
+AudioProcessingProperties::AudioProcessingProperties() = default;
+AudioProcessingProperties::AudioProcessingProperties(
+    const AudioProcessingProperties& other) = default;
+AudioProcessingProperties& AudioProcessingProperties::operator=(
+    const AudioProcessingProperties& other) = default;
+AudioProcessingProperties::AudioProcessingProperties(
+    AudioProcessingProperties&& other) = default;
+AudioProcessingProperties& AudioProcessingProperties::operator=(
+    AudioProcessingProperties&& other) = default;
+AudioProcessingProperties::~AudioProcessingProperties() = default;
+
 EchoInformation::EchoInformation()
     : delay_stats_time_ms_(0),
       echo_frames_received_(false),
diff --git a/content/renderer/media/media_stream_audio_processor_options.h b/content/renderer/media/media_stream_audio_processor_options.h
index f9e61675..c891c8c 100644
--- a/content/renderer/media/media_stream_audio_processor_options.h
+++ b/content/renderer/media/media_stream_audio_processor_options.h
@@ -6,12 +6,14 @@
 #define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_PROCESSOR_OPTIONS_H_
 
 #include <string>
+#include <vector>
 
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
 #include "content/public/common/media_stream_request.h"
+#include "media/base/audio_point.h"
 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
 #include "third_party/webrtc/api/mediastreaminterface.h"
 #include "third_party/webrtc/media/base/mediachannel.h"
@@ -30,6 +32,7 @@
 
 // A helper class to parse audio constraints from a blink::WebMediaConstraints
 // object.
+// TODO(guidou): Remove this class. http://crbug.com/706408
 class CONTENT_EXPORT MediaAudioConstraints {
  public:
   // Constraint keys used by audio processing.
@@ -94,6 +97,38 @@
   bool default_audio_processing_constraint_value_;
 };
 
+// Simple struct with audio-processing properties. Will substitute
+// MediaAudioConstraints once the old constraints-processing algorithm is
+// removed. http://crbug.com/706408
+struct CONTENT_EXPORT AudioProcessingProperties {
+  // Creates an AudioProcessingProperties object with fields initialized to
+  // their default values.
+  AudioProcessingProperties();
+  AudioProcessingProperties(const AudioProcessingProperties& other);
+  AudioProcessingProperties& operator=(const AudioProcessingProperties& other);
+  AudioProcessingProperties(AudioProcessingProperties&& other);
+  AudioProcessingProperties& operator=(AudioProcessingProperties&& other);
+  ~AudioProcessingProperties();
+
+  bool enable_sw_echo_cancellation = true;
+  bool disable_hw_echo_cancellation = false;
+  bool goog_audio_mirroring = false;
+  bool goog_auto_gain_control = true;
+  bool goog_experimental_echo_cancellation =
+#if defined(OS_ANDROID)
+      false;
+#else
+      true;
+#endif
+  bool goog_typing_noise_detection = true;
+  bool goog_noise_suppression = true;
+  bool goog_experimental_noise_suppression = true;
+  bool goog_beamforming = true;
+  bool goog_highpass_filter = true;
+  bool goog_experimental_auto_gain_control = true;
+  std::vector<media::Point> goog_array_geometry;
+};
+
 // A helper class to log echo information in general and Echo Cancellation
 // quality in particular.
 class CONTENT_EXPORT EchoInformation {
diff --git a/content/renderer/media/media_stream_constraints_util.cc b/content/renderer/media/media_stream_constraints_util.cc
index a5c3b0c..4a22c96 100644
--- a/content/renderer/media/media_stream_constraints_util.cc
+++ b/content/renderer/media/media_stream_constraints_util.cc
@@ -104,7 +104,9 @@
 VideoCaptureSettings::VideoCaptureSettings() : VideoCaptureSettings("") {}
 
 VideoCaptureSettings::VideoCaptureSettings(const char* failed_constraint_name)
-    : failed_constraint_name_(failed_constraint_name) {}
+    : failed_constraint_name_(failed_constraint_name) {
+  DCHECK(failed_constraint_name_);
+}
 
 VideoCaptureSettings::VideoCaptureSettings(
     std::string device_id,
@@ -140,6 +142,37 @@
 VideoCaptureSettings& VideoCaptureSettings::operator=(
     VideoCaptureSettings&& other) = default;
 
+AudioCaptureSettings::AudioCaptureSettings() : AudioCaptureSettings("") {}
+
+AudioCaptureSettings::AudioCaptureSettings(const char* failed_constraint_name)
+    : failed_constraint_name_(failed_constraint_name) {
+  DCHECK(failed_constraint_name_);
+}
+
+AudioCaptureSettings::AudioCaptureSettings(
+    std::string device_id,
+    const media::AudioParameters& audio_parameters,
+    bool enable_hotword,
+    bool disable_local_echo,
+    bool enable_automatic_output_device_selection,
+    const AudioProcessingProperties& audio_processing_properties)
+    : failed_constraint_name_(nullptr),
+      device_id_(std::move(device_id)),
+      audio_parameters_(audio_parameters),
+      hotword_enabled_(enable_hotword),
+      disable_local_echo_(disable_local_echo),
+      render_to_associated_sink_(enable_automatic_output_device_selection),
+      audio_processing_properties_(audio_processing_properties) {}
+
+AudioCaptureSettings::AudioCaptureSettings(const AudioCaptureSettings& other) =
+    default;
+AudioCaptureSettings& AudioCaptureSettings::operator=(
+    const AudioCaptureSettings& other) = default;
+AudioCaptureSettings::AudioCaptureSettings(AudioCaptureSettings&& other) =
+    default;
+AudioCaptureSettings& AudioCaptureSettings::operator=(
+    AudioCaptureSettings&& other) = default;
+
 bool GetConstraintValueAsBoolean(
     const blink::WebMediaConstraints& constraints,
     const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*picker,
@@ -211,6 +244,25 @@
   return rtc::Optional<bool>();
 }
 
+std::string GetMediaStreamSource(
+    const blink::WebMediaConstraints& constraints) {
+  std::string source;
+  if (constraints.Basic().media_stream_source.HasIdeal() &&
+      constraints.Basic().media_stream_source.Exact().size() > 0) {
+    source = constraints.Basic().media_stream_source.Ideal()[0].Utf8();
+  }
+  if (constraints.Basic().media_stream_source.HasExact() &&
+      constraints.Basic().media_stream_source.Exact().size() > 0) {
+    source = constraints.Basic().media_stream_source.Exact()[0].Utf8();
+  }
+
+  return source;
+}
+
+bool IsDeviceCapture(const blink::WebMediaConstraints& constraints) {
+  return GetMediaStreamSource(constraints).empty();
+}
+
 VideoTrackAdapterSettings SelectVideoTrackAdapterSettings(
     const blink::WebMediaTrackConstraintSet& basic_constraint_set,
     const ResolutionSet& resolution_set,
@@ -250,4 +302,26 @@
       track_max_aspect_ratio, track_max_frame_rate, expected_native_size);
 }
 
+double NumericConstraintFitnessDistance(double value1, double value2) {
+  if (std::fabs(value1 - value2) <= blink::DoubleConstraint::kConstraintEpsilon)
+    return 0.0;
+
+  return std::fabs(value1 - value2) /
+         std::max(std::fabs(value1), std::fabs(value2));
+}
+
+double StringConstraintFitnessDistance(
+    const blink::WebString& value,
+    const blink::StringConstraint& constraint) {
+  if (!constraint.HasIdeal())
+    return 0.0;
+
+  for (auto& ideal_value : constraint.Ideal()) {
+    if (value == ideal_value)
+      return 0.0;
+  }
+
+  return 1.0;
+}
+
 }  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util.h b/content/renderer/media/media_stream_constraints_util.h
index 1c666a3..4c238a5 100644
--- a/content/renderer/media/media_stream_constraints_util.h
+++ b/content/renderer/media/media_stream_constraints_util.h
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "content/common/content_export.h"
 #include "content/common/media/media_devices.mojom.h"
+#include "content/renderer/media/media_stream_audio_processor_options.h"
 #include "content/renderer/media/video_track_adapter.h"
 #include "media/capture/video_capture_types.h"
 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
@@ -50,6 +51,9 @@
 //   * min_frame_rate and max_frame_rate: used to control frame refreshes in
 //     screen-capture tracks sent to a peer connection. Derived from the
 //     frameRate constraint.
+// If SelectSettings fails, the HasValue() method returns false and
+// failed_constraint_name() returns the name of one of the (possibly multiple)
+// constraints that could not be satisfied.
 class CONTENT_EXPORT VideoCaptureSettings {
  public:
   // Creates an object without value and with an empty failed constraint name.
@@ -57,8 +61,8 @@
 
   // Creates an object without value and with the given
   // |failed_constraint_name|. Does not take ownership of
-  // |failed_constraint_name|, so it must be null or point to a string that
-  // remains accessible.
+  // |failed_constraint_name|, so it must point to a string that remains
+  // accessible. |failed_constraint_name| must be non-null.
   explicit VideoCaptureSettings(const char* failed_constraint_name);
 
   // Creates an object with the given values.
@@ -140,6 +144,95 @@
   base::Optional<double> max_frame_rate_;
 };
 
+// This class represents the output the SelectSettings algorithm for audio
+// constraints (see https://w3c.github.io/mediacapture-main/#dfn-selectsettings)
+// The input to SelectSettings is a user-supplied constraints object, and its
+// output is a set of implementation-specific settings that are used to
+// configure other Chromium objects such as sources, tracks and sinks so that
+// they work in the way indicated by the specification. AudioCaptureSettings may
+// also be used to implement other constraints-related functionality, such as
+// the getSettings() function.
+// The following fields are used to control MediaStreamVideoSource objects:
+//   * device_id: used for device selection and obtained from the deviceId
+//   * device_parameters: these are the hardware parameters for the device
+//     selected by SelectSettings. They can be used to verify that the
+//     parameters with which the audio stream is actually created corresponds
+//     to what SelectSettings selected. It can also be used to implement
+//     getSettings() for device-related properties such as sampleRate and
+//     channelCount.
+// The following fields are used to control various audio features:
+//   * hotword_enabled
+//   * disable_local_echo
+//   * render_to_associated_sink
+// The audio_properties field is used to control the audio-processing module,
+// which provides features such as software-based echo cancellation.
+// If SelectSettings fails, the HasValue() method returns false and
+// failed_constraint_name() returns the name of one of the (possibly multiple)
+// constraints that could not be satisfied.
+class CONTENT_EXPORT AudioCaptureSettings {
+ public:
+  // Creates an object without value and with an empty failed constraint name.
+  AudioCaptureSettings();
+
+  // Creates an object without value and with the given
+  // |failed_constraint_name|. Does not take ownership of
+  // |failed_constraint_name|, so it must point to a string that remains
+  // accessible. |failed_constraint_name| must be non-null.
+  explicit AudioCaptureSettings(const char* failed_constraint_name);
+
+  // Creates an object with the given values.
+  explicit AudioCaptureSettings(
+      std::string device_id,
+      const media::AudioParameters& audio_parameters,
+      bool enable_hotword,
+      bool disable_local_echo,
+      bool enable_automatic_output_device_selection,
+      const AudioProcessingProperties& audio_processing_properties);
+  AudioCaptureSettings(const AudioCaptureSettings& other);
+  AudioCaptureSettings& operator=(const AudioCaptureSettings& other);
+  AudioCaptureSettings(AudioCaptureSettings&& other);
+  AudioCaptureSettings& operator=(AudioCaptureSettings&& other);
+
+  bool HasValue() const { return failed_constraint_name_ == nullptr; }
+
+  // Accessors.
+  const char* failed_constraint_name() const { return failed_constraint_name_; }
+  const std::string& device_id() const {
+    DCHECK(HasValue());
+    return device_id_;
+  }
+  // This field is meaningless in content capture.
+  const media::AudioParameters& device_parameters() const {
+    DCHECK(HasValue());
+    return audio_parameters_;
+  }
+  bool hotword_enabled() const {
+    DCHECK(HasValue());
+    return hotword_enabled_;
+  }
+  bool disable_local_echo() const {
+    DCHECK(HasValue());
+    return disable_local_echo_;
+  }
+  bool render_to_associated_sink() const {
+    DCHECK(HasValue());
+    return render_to_associated_sink_;
+  }
+  AudioProcessingProperties audio_processing_properties() const {
+    DCHECK(HasValue());
+    return audio_processing_properties_;
+  }
+
+ private:
+  const char* failed_constraint_name_;
+  std::string device_id_;
+  media::AudioParameters audio_parameters_;
+  bool hotword_enabled_;
+  bool disable_local_echo_;
+  bool render_to_associated_sink_;
+  AudioProcessingProperties audio_processing_properties_;
+};
+
 // Method to get boolean value of constraint with |name| from constraints.
 // Returns true if the constraint is specified in either mandatory or optional
 // constraints.
@@ -220,6 +313,22 @@
   return constraint.HasExact() ? constraint.Exact() : constraint.Min();
 }
 
+// If |value| is outside the range of |constraint|, returns the name of the
+// failed constraint. Otherwise, returns nullptr. The return value converts to
+// bool in the expected way.
+template <typename NumericConstraintType, typename ValueType>
+const char* IsOutsideConstraintRange(NumericConstraintType constraint,
+                                     ValueType value) {
+  return (ConstraintHasMin(constraint) && value < ConstraintMin(constraint)) ||
+                 (ConstraintHasMax(constraint) &&
+                  value > ConstraintMax(constraint))
+             ? constraint.GetName()
+             : nullptr;
+}
+
+std::string GetMediaStreamSource(const blink::WebMediaConstraints& constraints);
+bool IsDeviceCapture(const blink::WebMediaConstraints& constraints);
+
 // This function selects track settings from a set of candidate resolutions and
 // frame rates, given the source video-capture format and ideal values.
 // The output are settings for a VideoTrackAdapter, which can adjust the
@@ -249,6 +358,17 @@
     const media::VideoCaptureFormat& source_format,
     bool expect_source_native_size);
 
+// Generic distance function between two values for numeric constraints. Based
+// on the fitness-distance function described in
+// https://w3c.github.io/mediacapture-main/#dfn-fitness-distance
+double NumericConstraintFitnessDistance(double value1, double value2);
+
+// Fitness distance between |value| and |constraint|.
+// Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
+double StringConstraintFitnessDistance(
+    const blink::WebString& value,
+    const blink::StringConstraint& constraint);
+
 }  // namespace content
 
 #endif  // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_H_
diff --git a/content/renderer/media/media_stream_constraints_util_audio.cc b/content/renderer/media/media_stream_constraints_util_audio.cc
new file mode 100644
index 0000000..155d7069
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_audio.cc
@@ -0,0 +1,614 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_constraints_util_audio.h"
+
+#include <algorithm>
+#include <cmath>
+#include <utility>
+#include <vector>
+
+#include "content/common/media/media_stream_options.h"
+#include "content/renderer/media/media_stream_constraints_util_sets.h"
+#include "content/renderer/media/media_stream_video_source.h"
+#include "media/base/limits.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace content {
+
+namespace {
+
+// This class has the same data as ::mojom::AudioInputDeviceCapabilities, but
+// adds extra operations to simplify the device-selection code.
+class AudioDeviceInfo {
+ public:
+  // This constructor is intended for device capture.
+  explicit AudioDeviceInfo(
+      const ::mojom::AudioInputDeviceCapabilitiesPtr& device_info)
+      : device_id_(device_info->device_id),
+        parameters_(device_info->parameters) {
+    DCHECK(parameters_.IsValid());
+  }
+
+  // This constructor is intended for content capture, where constraints on
+  // audio parameters are not supported.
+  explicit AudioDeviceInfo(std::string device_id)
+      : device_id_(std::move(device_id)) {}
+
+  bool operator==(const AudioDeviceInfo& other) const {
+    return device_id_ == other.device_id_;
+  }
+
+  // Accessors
+  const std::string& device_id() const { return device_id_; }
+  const media::AudioParameters& parameters() const { return parameters_; }
+
+  // Convenience accessors
+  int SampleRate() const {
+    DCHECK(parameters_.IsValid());
+    return parameters_.sample_rate();
+  }
+  int SampleSize() const {
+    DCHECK(parameters_.IsValid());
+    return parameters_.bits_per_sample();
+  }
+  int ChannelCount() const {
+    DCHECK(parameters_.IsValid());
+    return parameters_.channels();
+  }
+  int Effects() const {
+    DCHECK(parameters_.IsValid());
+    return parameters_.effects();
+  }
+
+ private:
+  std::string device_id_;
+  media::AudioParameters parameters_;
+};
+
+using AudioDeviceSet = DiscreteSet<AudioDeviceInfo>;
+
+AudioDeviceSet AudioDeviceSetForDeviceCapture(
+    const blink::WebMediaTrackConstraintSet& constraint_set,
+    const AudioDeviceCaptureCapabilities& capabilities,
+    const char** failed_constraint_name) {
+  std::vector<AudioDeviceInfo> result;
+  *failed_constraint_name = "";
+  for (auto& device_capabilities : capabilities) {
+    if (!constraint_set.device_id.Matches(
+            blink::WebString::FromASCII(device_capabilities->device_id))) {
+      if (failed_constraint_name)
+        *failed_constraint_name = constraint_set.device_id.GetName();
+      continue;
+    }
+    *failed_constraint_name =
+        IsOutsideConstraintRange(constraint_set.sample_rate,
+                                 device_capabilities->parameters.sample_rate());
+    if (*failed_constraint_name)
+      continue;
+
+    *failed_constraint_name = IsOutsideConstraintRange(
+        constraint_set.sample_size,
+        device_capabilities->parameters.bits_per_sample());
+    if (*failed_constraint_name)
+      continue;
+
+    *failed_constraint_name =
+        IsOutsideConstraintRange(constraint_set.channel_count,
+                                 device_capabilities->parameters.channels());
+    if (*failed_constraint_name)
+      continue;
+
+    result.push_back(AudioDeviceInfo(device_capabilities));
+  }
+
+  if (!result.empty())
+    *failed_constraint_name = nullptr;
+
+  return AudioDeviceSet(result);
+}
+
+AudioDeviceSet AudioDeviceSetForContentCapture(
+    const blink::WebMediaTrackConstraintSet& constraint_set,
+    const char** failed_constraint_name = nullptr) {
+  if (!constraint_set.device_id.HasExact())
+    return AudioDeviceSet::UniversalSet();
+
+  std::vector<AudioDeviceInfo> result;
+  for (auto& device_id : constraint_set.device_id.Exact())
+    result.push_back(AudioDeviceInfo(device_id.Utf8()));
+
+  return AudioDeviceSet(result);
+}
+
+// This class represents a set of possible candidate settings.
+// The SelectSettings algorithm starts with a set containing all possible
+// candidates based on hardware capabilities and/or allowed values for supported
+// properties. The is then reduced progressively as the basic and advanced
+// constraint sets are applied.
+// In the end, if the set of candidates is empty, SelectSettings fails.
+// If not, the ideal values (if any) or tie breaker rules are used to select
+// the final settings based on the candidates that survived the application
+// of the constraint sets.
+// This class is implemented as a collection of more specific sets for the
+// various supported properties. If any of the specific sets is empty, the
+// whole AudioCaptureCandidates set is considered empty as well.
+class AudioCaptureCandidates {
+ public:
+  enum BoolConstraint {
+    // Constraints not related to audio processing.
+    HOTWORD_ENABLED,
+    DISABLE_LOCAL_ECHO,
+    RENDER_TO_ASSOCIATED_SINK,
+
+    // Constraints that enable/disable audio processing.
+    ECHO_CANCELLATION,
+    GOOG_ECHO_CANCELLATION,
+
+    // Constraints that control audio-processing behavior.
+    GOOG_AUDIO_MIRRORING,
+    GOOG_AUTO_GAIN_CONTROL,
+    GOOG_EXPERIMENTAL_ECHO_CANCELLATION,
+    GOOG_TYPING_NOISE_DETECTION,
+    GOOG_NOISE_SUPPRESSION,
+    GOOG_EXPERIMENTAL_NOISE_SUPPRESSION,
+    GOOG_BEAMFORMING,
+    GOOG_HIGHPASS_FILTER,
+    GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL,
+    NUM_BOOL_CONSTRAINTS
+  };
+
+  AudioCaptureCandidates();
+
+  AudioCaptureCandidates(
+      const blink::WebMediaTrackConstraintSet& constraint_set,
+      const AudioDeviceCaptureCapabilities& capabilities,
+      bool is_device_capture);
+
+  // Set operations.
+  bool IsEmpty() const { return failed_constraint_name_ != nullptr; }
+  AudioCaptureCandidates Intersection(const AudioCaptureCandidates& other);
+
+  // Accessors.
+  const char* failed_constraint_name() const { return failed_constraint_name_; }
+  const AudioDeviceSet& audio_device_set() const { return audio_device_set_; }
+  const DiscreteSet<std::string>& goog_array_geometry_set() const {
+    return goog_array_geometry_set_;
+  }
+
+  // Accessor for boolean sets.
+  const DiscreteSet<bool>& GetBoolSet(BoolConstraint property) const {
+    DCHECK_GE(property, 0);
+    DCHECK_LT(property, NUM_BOOL_CONSTRAINTS);
+    return bool_sets_[property];
+  }
+
+  // Convenience accessors.
+  const DiscreteSet<bool>& hotword_enabled_set() const {
+    return bool_sets_[HOTWORD_ENABLED];
+  }
+  const DiscreteSet<bool>& disable_local_echo_set() const {
+    return bool_sets_[DISABLE_LOCAL_ECHO];
+  }
+  const DiscreteSet<bool>& render_to_associated_sink_set() const {
+    return bool_sets_[RENDER_TO_ASSOCIATED_SINK];
+  }
+  const DiscreteSet<bool>& echo_cancellation_set() const {
+    return bool_sets_[ECHO_CANCELLATION];
+  }
+  const DiscreteSet<bool>& goog_echo_cancellation_set() const {
+    return bool_sets_[GOOG_ECHO_CANCELLATION];
+  }
+  const DiscreteSet<bool>& goog_audio_mirroring_set() const {
+    return bool_sets_[GOOG_AUDIO_MIRRORING];
+  }
+
+ private:
+  void MaybeUpdateFailedNonDeviceConstraintName();
+  void CheckContradictoryEchoCancellation();
+
+  // Maps BoolConstraint values to fields in blink::WebMediaTrackConstraintSet.
+  static const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*
+      kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS];
+
+  const char* failed_constraint_name_;
+
+  AudioDeviceSet audio_device_set_;  // Device-related constraints.
+  std::array<DiscreteSet<bool>, NUM_BOOL_CONSTRAINTS> bool_sets_;
+  DiscreteSet<std::string> goog_array_geometry_set_;
+};
+
+const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*
+    AudioCaptureCandidates::kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS] = {
+        &blink::WebMediaTrackConstraintSet::hotword_enabled,
+        &blink::WebMediaTrackConstraintSet::disable_local_echo,
+        &blink::WebMediaTrackConstraintSet::render_to_associated_sink,
+        &blink::WebMediaTrackConstraintSet::echo_cancellation,
+        &blink::WebMediaTrackConstraintSet::goog_echo_cancellation,
+        &blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
+        &blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
+        &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation,
+        &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
+        &blink::WebMediaTrackConstraintSet::goog_noise_suppression,
+        &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression,
+        &blink::WebMediaTrackConstraintSet::goog_beamforming,
+        &blink::WebMediaTrackConstraintSet::goog_highpass_filter,
+        &blink::WebMediaTrackConstraintSet::
+            goog_experimental_auto_gain_control};
+
+// directly mapped boolean sets to audio properties
+struct BoolSetPropertyEntry {
+  DiscreteSet<bool> AudioCaptureCandidates::*bool_set;
+  bool AudioProcessingProperties::*bool_field;
+  bool default_value;
+};
+
+AudioCaptureCandidates::AudioCaptureCandidates()
+    : failed_constraint_name_(nullptr) {}
+
+AudioCaptureCandidates::AudioCaptureCandidates(
+    const blink::WebMediaTrackConstraintSet& constraint_set,
+    const AudioDeviceCaptureCapabilities& capabilities,
+    bool is_device_capture)
+    : failed_constraint_name_(nullptr),
+      audio_device_set_(
+          is_device_capture
+              ? AudioDeviceSetForDeviceCapture(constraint_set,
+                                               capabilities,
+                                               &failed_constraint_name_)
+              : AudioDeviceSetForContentCapture(constraint_set,
+                                                &failed_constraint_name_)),
+      goog_array_geometry_set_(
+          StringSetFromConstraint(constraint_set.goog_array_geometry)) {
+  for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
+    bool_sets_[i] =
+        BoolSetFromConstraint(constraint_set.*kBlinkBoolConstraintFields[i]);
+  }
+  MaybeUpdateFailedNonDeviceConstraintName();
+}
+
+AudioCaptureCandidates AudioCaptureCandidates::Intersection(
+    const AudioCaptureCandidates& other) {
+  AudioCaptureCandidates intersection;
+  intersection.audio_device_set_ =
+      audio_device_set_.Intersection(other.audio_device_set_);
+  if (intersection.audio_device_set_.IsEmpty()) {
+    // To mark the intersection as empty, it is necessary to assign a
+    // a non-null value to |failed_constraint_name_|. The specific value
+    // for an intersection does not actually matter, since the intersection
+    // is discarded if empty.
+    intersection.failed_constraint_name_ = "some device constraint";
+    return intersection;
+  }
+  for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
+    intersection.bool_sets_[i] =
+        bool_sets_[i].Intersection(other.bool_sets_[i]);
+  }
+  intersection.goog_array_geometry_set_ =
+      goog_array_geometry_set_.Intersection(other.goog_array_geometry_set_);
+  intersection.MaybeUpdateFailedNonDeviceConstraintName();
+
+  return intersection;
+}
+
+void AudioCaptureCandidates::MaybeUpdateFailedNonDeviceConstraintName() {
+  blink::WebMediaTrackConstraintSet constraint_set;
+  for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
+    if (bool_sets_[i].IsEmpty()) {
+      failed_constraint_name_ =
+          (constraint_set.*kBlinkBoolConstraintFields[i]).GetName();
+    }
+  }
+  if (goog_array_geometry_set_.IsEmpty())
+    failed_constraint_name_ = constraint_set.goog_array_geometry.GetName();
+
+  CheckContradictoryEchoCancellation();
+}
+
+void AudioCaptureCandidates::CheckContradictoryEchoCancellation() {
+  DiscreteSet<bool> echo_cancellation_intersection =
+      bool_sets_[ECHO_CANCELLATION].Intersection(
+          bool_sets_[GOOG_ECHO_CANCELLATION]);
+  // echoCancellation and googEchoCancellation constraints should not
+  // contradict each other. Mark the set as empty if they do.
+  if (echo_cancellation_intersection.IsEmpty()) {
+    failed_constraint_name_ =
+        blink::WebMediaTrackConstraintSet().echo_cancellation.GetName();
+  }
+}
+
+// Fitness function for constraints involved in device selection.
+//  Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance
+double DeviceInfoFitness(
+    bool is_device_capture,
+    const AudioDeviceInfo& device_info,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+  double fitness = 0.0;
+  fitness += StringConstraintFitnessDistance(
+      blink::WebString::FromASCII(device_info.device_id()),
+      basic_constraint_set.device_id);
+
+  if (!is_device_capture)
+    return fitness;
+
+  if (basic_constraint_set.sample_rate.HasIdeal()) {
+    fitness += NumericConstraintFitnessDistance(
+        device_info.SampleRate(), basic_constraint_set.sample_rate.Ideal());
+  }
+
+  if (basic_constraint_set.sample_size.HasIdeal()) {
+    fitness += NumericConstraintFitnessDistance(
+        device_info.SampleSize(), basic_constraint_set.sample_size.Ideal());
+  }
+
+  if (basic_constraint_set.channel_count.HasIdeal()) {
+    fitness += NumericConstraintFitnessDistance(
+        device_info.ChannelCount(), basic_constraint_set.channel_count.Ideal());
+  }
+
+  return fitness;
+}
+
+AudioDeviceInfo SelectDevice(
+    const AudioDeviceSet& audio_device_set,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set,
+    const std::string& default_device_id,
+    bool is_device_capture) {
+  DCHECK(!audio_device_set.IsEmpty());
+  if (audio_device_set.is_universal()) {
+    DCHECK(!is_device_capture);
+    std::string device_id;
+    if (basic_constraint_set.device_id.HasIdeal()) {
+      device_id = basic_constraint_set.device_id.Ideal().begin()->Utf8();
+    }
+    return AudioDeviceInfo(std::move(device_id));
+  }
+
+  std::vector<double> best_fitness({HUGE_VAL, HUGE_VAL});
+  auto best_candidate = audio_device_set.elements().end();
+  for (auto it = audio_device_set.elements().begin();
+       it != audio_device_set.elements().end(); ++it) {
+    std::vector<double> fitness;
+    // First criterion is spec-based fitness function. Second criterion is
+    // being the default device.
+    fitness.push_back(
+        DeviceInfoFitness(is_device_capture, *it, basic_constraint_set));
+    fitness.push_back(it->device_id() == default_device_id ? 0.0 : HUGE_VAL);
+    if (fitness < best_fitness) {
+      best_fitness = std::move(fitness);
+      best_candidate = it;
+    }
+  }
+  DCHECK(best_candidate != audio_device_set.elements().end());
+  return *best_candidate;
+}
+
+bool SelectBool(const DiscreteSet<bool>& set,
+                const blink::BooleanConstraint& constraint,
+                bool default_value) {
+  DCHECK(!set.IsEmpty());
+  if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) {
+    return constraint.Ideal();
+  }
+
+  // Return default value if unconstrained.
+  if (set.is_universal()) {
+    return default_value;
+  }
+  DCHECK_EQ(set.elements().size(), 1U);
+  return set.FirstElement();
+}
+
+base::Optional<bool> SelectOptionalBool(
+    const DiscreteSet<bool>& set,
+    const blink::BooleanConstraint& constraint) {
+  DCHECK(!set.IsEmpty());
+  if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) {
+    return constraint.Ideal();
+  }
+
+  // Return no value if unconstrained.
+  if (set.is_universal()) {
+    return base::Optional<bool>();
+  }
+  DCHECK_EQ(set.elements().size(), 1U);
+  return set.FirstElement();
+}
+
+base::Optional<std::string> SelectOptionalString(
+    const DiscreteSet<std::string>& set,
+    const blink::StringConstraint& constraint) {
+  DCHECK(!set.IsEmpty());
+  if (constraint.HasIdeal()) {
+    for (const auto& ideal_candidate : constraint.Ideal()) {
+      std::string candidate = ideal_candidate.Utf8();
+      if (set.Contains(candidate)) {
+        return candidate;
+      }
+    }
+  }
+
+  // Return no value if unconstrained.
+  if (set.is_universal()) {
+    return base::Optional<std::string>();
+  }
+  return set.FirstElement();
+}
+
+bool SelectEnableSwEchoCancellation(
+    base::Optional<bool> echo_cancellation,
+    base::Optional<bool> goog_echo_cancellation,
+    const media::AudioParameters& audio_parameters,
+    bool default_audio_processing_value) {
+  // If there is hardware echo cancellation, return false.
+  if (audio_parameters.IsValid() &&
+      (audio_parameters.effects() & media::AudioParameters::ECHO_CANCELLER))
+    return false;
+  DCHECK(echo_cancellation && goog_echo_cancellation
+             ? *echo_cancellation == *goog_echo_cancellation
+             : true);
+  if (echo_cancellation)
+    return *echo_cancellation;
+  if (goog_echo_cancellation)
+    return *goog_echo_cancellation;
+
+  return default_audio_processing_value;
+}
+
+struct AudioPropertyConstraintPair {
+  bool AudioProcessingProperties::*audio_property;
+  AudioCaptureCandidates::BoolConstraint bool_set_index;
+  blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*constraint;
+};
+
+// Boolean audio properties that are mapped directly to a boolean constraint
+// and which are subject to the same processing.
+const AudioPropertyConstraintPair kAudioPropertyConstraintMap[] = {
+    {&AudioProcessingProperties::goog_auto_gain_control,
+     AudioCaptureCandidates::GOOG_AUTO_GAIN_CONTROL,
+     &blink::WebMediaTrackConstraintSet::goog_auto_gain_control},
+    {&AudioProcessingProperties::goog_experimental_echo_cancellation,
+     AudioCaptureCandidates::GOOG_EXPERIMENTAL_ECHO_CANCELLATION,
+     &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation},
+    {&AudioProcessingProperties::goog_typing_noise_detection,
+     AudioCaptureCandidates::GOOG_TYPING_NOISE_DETECTION,
+     &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection},
+    {&AudioProcessingProperties::goog_noise_suppression,
+     AudioCaptureCandidates::GOOG_NOISE_SUPPRESSION,
+     &blink::WebMediaTrackConstraintSet::goog_noise_suppression},
+    {&AudioProcessingProperties::goog_experimental_noise_suppression,
+     AudioCaptureCandidates::GOOG_EXPERIMENTAL_NOISE_SUPPRESSION,
+     &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression},
+    {&AudioProcessingProperties::goog_beamforming,
+     AudioCaptureCandidates::GOOG_BEAMFORMING,
+     &blink::WebMediaTrackConstraintSet::goog_beamforming},
+    {&AudioProcessingProperties::goog_highpass_filter,
+     AudioCaptureCandidates::GOOG_HIGHPASS_FILTER,
+     &blink::WebMediaTrackConstraintSet::goog_highpass_filter},
+    {&AudioProcessingProperties::goog_experimental_auto_gain_control,
+     AudioCaptureCandidates::GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL,
+     &blink::WebMediaTrackConstraintSet::goog_experimental_auto_gain_control}};
+
+AudioProcessingProperties SelectAudioProcessingProperties(
+    const AudioCaptureCandidates& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set,
+    const media::AudioParameters& audio_parameters,
+    bool is_device_capture) {
+  DCHECK(!candidates.IsEmpty());
+  base::Optional<bool> echo_cancellation =
+      SelectOptionalBool(candidates.echo_cancellation_set(),
+                         basic_constraint_set.echo_cancellation);
+  // Audio-processing properties are disabled by default for content capture, or
+  // if the |echo_cancellation| constraint is false.
+  bool default_audio_processing_value = true;
+  if (!is_device_capture || (echo_cancellation && !*echo_cancellation))
+    default_audio_processing_value = false;
+
+  base::Optional<bool> goog_echo_cancellation =
+      SelectOptionalBool(candidates.goog_echo_cancellation_set(),
+                         basic_constraint_set.goog_echo_cancellation);
+
+  AudioProcessingProperties properties;
+  properties.enable_sw_echo_cancellation = SelectEnableSwEchoCancellation(
+      echo_cancellation, goog_echo_cancellation, audio_parameters,
+      default_audio_processing_value);
+  properties.disable_hw_echo_cancellation =
+      (echo_cancellation && !*echo_cancellation) ||
+      (goog_echo_cancellation && !*goog_echo_cancellation);
+
+  properties.goog_audio_mirroring =
+      SelectBool(candidates.goog_audio_mirroring_set(),
+                 basic_constraint_set.goog_audio_mirroring,
+                 properties.goog_audio_mirroring);
+
+  for (auto& entry : kAudioPropertyConstraintMap) {
+    properties.*entry.audio_property = SelectBool(
+        candidates.GetBoolSet(entry.bool_set_index),
+        basic_constraint_set.*entry.constraint,
+        default_audio_processing_value && properties.*entry.audio_property);
+  }
+
+  base::Optional<std::string> array_geometry =
+      SelectOptionalString(candidates.goog_array_geometry_set(),
+                           basic_constraint_set.goog_array_geometry);
+  std::vector<media::Point> parsed_positions;
+  if (array_geometry)
+    parsed_positions = media::ParsePointsFromString(*array_geometry);
+  bool are_valid_parsed_positions =
+      !parsed_positions.empty() || (array_geometry && array_geometry->empty());
+  properties.goog_array_geometry = are_valid_parsed_positions
+                                       ? std::move(parsed_positions)
+                                       : audio_parameters.mic_positions();
+
+  return properties;
+}
+
+AudioCaptureSettings SelectResult(
+    const AudioCaptureCandidates& candidates,
+    const blink::WebMediaTrackConstraintSet& basic_constraint_set,
+    const std::string& default_device_id,
+    const std::string& media_stream_source) {
+  bool is_device_capture = media_stream_source.empty();
+  AudioDeviceInfo device_info =
+      SelectDevice(candidates.audio_device_set(), basic_constraint_set,
+                   default_device_id, is_device_capture);
+  bool hotword_enabled =
+      SelectBool(candidates.hotword_enabled_set(),
+                 basic_constraint_set.hotword_enabled, false);
+  bool disable_local_echo_default =
+      media_stream_source != kMediaStreamSourceDesktop;
+  bool disable_local_echo = SelectBool(candidates.disable_local_echo_set(),
+                                       basic_constraint_set.disable_local_echo,
+                                       disable_local_echo_default);
+  bool render_to_associated_sink =
+      SelectBool(candidates.render_to_associated_sink_set(),
+                 basic_constraint_set.render_to_associated_sink, false);
+
+  AudioProcessingProperties audio_processing_properties =
+      SelectAudioProcessingProperties(candidates, basic_constraint_set,
+                                      device_info.parameters(),
+                                      is_device_capture);
+
+  return AudioCaptureSettings(device_info.device_id(), device_info.parameters(),
+                              hotword_enabled, disable_local_echo,
+                              render_to_associated_sink,
+                              audio_processing_properties);
+}
+
+}  // namespace
+
+AudioCaptureSettings SelectSettingsAudioCapture(
+    const AudioDeviceCaptureCapabilities& capabilities,
+    const blink::WebMediaConstraints& constraints) {
+  std::string media_stream_source = GetMediaStreamSource(constraints);
+  bool is_device_capture = media_stream_source.empty();
+  if (is_device_capture && capabilities.empty())
+    return AudioCaptureSettings();
+
+  AudioCaptureCandidates candidates(constraints.Basic(), capabilities,
+                                    is_device_capture);
+  if (candidates.IsEmpty()) {
+    return AudioCaptureSettings(candidates.failed_constraint_name());
+  }
+
+  for (const auto& advanced_set : constraints.Advanced()) {
+    AudioCaptureCandidates advanced_candidates(advanced_set, capabilities,
+                                               is_device_capture);
+    AudioCaptureCandidates intersection =
+        candidates.Intersection(advanced_candidates);
+    if (!intersection.IsEmpty())
+      candidates = std::move(intersection);
+  }
+  DCHECK(!candidates.IsEmpty());
+
+  std::string default_device_id;
+  if (!capabilities.empty())
+    default_device_id = (*capabilities.begin())->device_id;
+
+  return SelectResult(candidates, constraints.Basic(), default_device_id,
+                      media_stream_source);
+}
+
+}  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util_audio.h b/content/renderer/media/media_stream_constraints_util_audio.h
new file mode 100644
index 0000000..c5e0859
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_audio.h
@@ -0,0 +1,102 @@
+// 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 CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_AUDIO_H_
+#define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_AUDIO_H_
+
+#include <string>
+#include <vector>
+
+#include "content/common/content_export.h"
+#include "content/common/media/media_devices.mojom.h"
+#include "content/renderer/media/media_stream_constraints_util.h"
+
+namespace blink {
+class WebMediaConstraints;
+}
+
+namespace content {
+
+using AudioDeviceCaptureCapabilities =
+    std::vector<::mojom::AudioInputDeviceCapabilitiesPtr>;
+
+// This function implements the SelectSettings algorithm for audio tracks as
+// described in https://w3c.github.io/mediacapture-main/#dfn-selectsettings
+// The algorithm starts with a set containing all possible candidate settings
+// based on hardware capabilities (passed via the |capabilities| parameter) and
+// supported values for properties not involved in device selection. Candidates
+// that do not support the basic constraint set from |constraints| are removed.
+// If the set of candidates is empty after this step, the function returns an
+// AudioCaptureSettings object without value and whose failed_constraint_name()
+// method returns the name of one of the (possibly many) constraints that could
+// not be satisfied or an empty string if the set of candidates was initially
+// empty (e.g., if there are no devices in the system).
+// After the basic constraint set is applied, advanced constraint sets are
+// applied. If no candidates can satisfy an advanced set, the advanced set is
+// ignored, otherwise the candidates that cannot satisfy the advanced set are
+// removed.
+// Once all constraint sets are applied, the result is selected from the
+// remaining candidates by giving preference to candidates closest to the ideal
+// values specified in the basic constraint set, or using default
+// implementation-specific values.
+// The result includes the following properties:
+//  * Device. A device is chosen using the device_id, sample_rate, sample_size,
+//    and channel_count constraints. If multiple devices satisfy the constraints
+//    preference is given to the default device (system defined or chosen by
+//    user preferences). If the default device is not included in the valid
+//    candidates, the first valid device in the list obtained by querying the
+//    system capabilities is chosen. For content capture, no real audio input
+//    devices are used and the sample_rate, sample_size and channel_count
+//    constraints are ignored. In content capture, the deviceId constraint is
+//    supported and is interpreted by the system as a string that indicates,
+//    for example, which tab to capture. Validation for that "device" ID is
+//    performed by the getUserMedia implementation. To decide between content
+//    or device capture, the value of the special media_stream_source constraint
+//    is used.
+//  * Audio features: the hotword_enabled, disable_local_echo and
+//    render_to_associated_sink constraints can be used to enable the
+//    corresponding audio feature. If not specified, their default value is
+//    false.
+//  * Audio processing. The remaining constraints are used to control audio
+//    processing. This is how audio-processing properties are set for device
+//    capture(see the content::AudioProcessingProperties struct) :
+//      - enable_sw_echo_cancellation: If the selected device has hardware echo
+//        cancellation, software echo cancellation is disabled regardless of
+//        any constraint values. Otherwise, it is enabled by default unless
+//        either the echo_cancellation or the goog_echo_cancellation constraint
+//        has a final value of false after applying all constraint sets. Note
+//        that if these constraints have contradictory values, SelectSettings
+//        fails and returns no value.
+//      - disable_hw_echo_cancellation: For devices that have hardware echo
+//        cancellation, the feature is disabled if the echo_cancellation or
+//        goog_echo_cancellation constraints have a final value of false.
+//      - goog_audio_mirroring: This property is mapped directly from the final
+//        value of the goog_audio_mirroring constraint. If no value is
+//        explicitly specified, the default value is false.
+//    The remaining audio-processing properties are directly mapped from the
+//    final value of the corresponding constraints. If no value is explicitly
+//    specified, the default value is the same as the final value of the
+//    echo_cancellation constraint.  If the echo_cancellation constraint is
+//    not explicitly specified, the default value is implementation defined
+//    (see content::AudioProcessingProperties).
+//    For content capture the rules are the same, but all properties are false
+//    by default, regardless of the value of the echo_cancellation constraint.
+//    Note that it is important to distinguish between audio properties and
+//    constraints. Constraints are an input to SelectSettings, while properties
+//    are part of the output. The value for most boolean properties comes
+//    directly from a corresponding boolean constraint, but this is not true for
+//    all constraints and properties. For example, the echo_cancellation and
+//    goog_echo_cancellation constraints  are not directly mapped to any
+//    property, but they, together with hardware characteristics, influence the
+//    enabling and disabling of software and hardware echo cancellation.
+//    Moreover, the echo_cancellation constraint influences most other
+//    audio-processing properties for which no explicit value is provided in
+//    their corresponding constraints.
+AudioCaptureSettings CONTENT_EXPORT
+SelectSettingsAudioCapture(const AudioDeviceCaptureCapabilities& capabilities,
+                           const blink::WebMediaConstraints& constraints);
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_AUDIO_H_
diff --git a/content/renderer/media/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/media_stream_constraints_util_audio_unittest.cc
new file mode 100644
index 0000000..db06ed6
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_audio_unittest.cc
@@ -0,0 +1,1396 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_constraints_util_audio.h"
+
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "content/common/media/media_devices.mojom.h"
+#include "content/renderer/media/mock_constraint_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace content {
+
+namespace {
+
+using BoolSetFunction = void (blink::BooleanConstraint::*)(bool);
+using StringSetFunction =
+    void (blink::StringConstraint::*)(const blink::WebString&);
+using MockFactoryAccessor =
+    blink::WebMediaTrackConstraintSet& (MockConstraintFactory::*)();
+
+const BoolSetFunction kBoolSetFunctions[] = {
+    &blink::BooleanConstraint::SetExact, &blink::BooleanConstraint::SetIdeal,
+};
+
+const StringSetFunction kStringSetFunctions[] = {
+    &blink::StringConstraint::SetExact, &blink::StringConstraint::SetIdeal,
+};
+
+const MockFactoryAccessor kFactoryAccessors[] = {
+    &MockConstraintFactory::basic, &MockConstraintFactory::AddAdvanced};
+
+const bool kBoolValues[] = {true, false};
+
+using AudioSettingsBoolMembers =
+    std::vector<bool (AudioCaptureSettings::*)() const>;
+using AudioPropertiesBoolMembers =
+    std::vector<bool AudioProcessingProperties::*>;
+
+template <typename T>
+static bool Contains(const std::vector<T>& vector, T value) {
+  auto it = std::find(vector.begin(), vector.end(), value);
+  return it != vector.end();
+}
+
+}  // namespace
+
+class MediaStreamConstraintsUtilAudioTest
+    : public testing::TestWithParam<std::string> {
+ public:
+  void SetUp() override {
+    ResetFactory();
+    if (!IsDeviceCapture())
+      return;
+
+    ::mojom::AudioInputDeviceCapabilitiesPtr device =
+        ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = "default_device";
+    device->parameters = media::AudioParameters(
+        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+        media::CHANNEL_LAYOUT_STEREO,
+        media::AudioParameters::kAudioCDSampleRate, 16, 1000);
+    capabilities_.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = "mono_phone_device";
+    device->parameters = media::AudioParameters(
+        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+        media::CHANNEL_LAYOUT_MONO,
+        media::AudioParameters::kTelephoneSampleRate, 16, 1000);
+    capabilities_.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = "hw_echo_canceller_device";
+    device->parameters = media::AudioParameters(
+        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+        media::CHANNEL_LAYOUT_STEREO,
+        media::AudioParameters::kAudioCDSampleRate, 24, 1000);
+    device->parameters.set_effects(media::AudioParameters::ECHO_CANCELLER);
+    capabilities_.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = "octagonal_device";
+    device->parameters = media::AudioParameters(
+        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+        media::CHANNEL_LAYOUT_OCTAGONAL, 100000, 8, 1000);
+    capabilities_.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = "geometry device";
+    device->parameters = media::AudioParameters(
+        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+        media::CHANNEL_LAYOUT_STEREO,
+        media::AudioParameters::kAudioCDSampleRate, 16, 1000);
+    device->parameters.set_mic_positions(kMicPositions);
+    capabilities_.push_back(std::move(device));
+
+    default_device_ = capabilities_[0].get();
+    mono_phone_device_ = capabilities_[1].get();
+    hw_echo_canceller_device_ = capabilities_[2].get();
+    octagonal_device_ = capabilities_[3].get();
+    geometry_device_ = capabilities_[4].get();
+  }
+
+ protected:
+  void SetMediaStreamSource(const std::string& source) {}
+
+  void ResetFactory() {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().media_stream_source.SetExact(
+        blink::WebString::FromASCII(GetParam()));
+  }
+
+  std::string GetMediaStreamSource() { return GetParam(); }
+  bool IsDeviceCapture() { return GetMediaStreamSource().empty(); }
+
+  AudioCaptureSettings SelectSettings() {
+    blink::WebMediaConstraints constraints =
+        constraint_factory_.CreateWebMediaConstraints();
+    return SelectSettingsAudioCapture(capabilities_, constraints);
+  }
+
+  // When googExperimentalEchoCancellation is not explicitly set, its default
+  // value is always false on Android. On other platforms it behaves like other
+  // audio-processing properties.
+  void CheckGoogExperimentalEchoCancellationDefault(
+      const AudioProcessingProperties& properties,
+      bool value) {
+#if defined(OS_ANDROID)
+    EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
+#else
+    EXPECT_EQ(value, properties.goog_experimental_echo_cancellation);
+#endif
+  }
+
+  void CheckBoolDefaultsDeviceCapture(
+      const AudioSettingsBoolMembers& exclude_main_settings,
+      const AudioPropertiesBoolMembers& exclude_audio_properties,
+      const AudioCaptureSettings& result) {
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::hotword_enabled)) {
+      EXPECT_FALSE(result.hotword_enabled());
+    }
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::disable_local_echo)) {
+      EXPECT_TRUE(result.disable_local_echo());
+    }
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::render_to_associated_sink)) {
+      EXPECT_FALSE(result.render_to_associated_sink());
+    }
+
+    const auto& properties = result.audio_processing_properties();
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::enable_sw_echo_cancellation)) {
+      EXPECT_TRUE(properties.enable_sw_echo_cancellation);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::disable_hw_echo_cancellation)) {
+      EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_audio_mirroring)) {
+      EXPECT_FALSE(properties.goog_audio_mirroring);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_auto_gain_control)) {
+      EXPECT_TRUE(properties.goog_auto_gain_control);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_echo_cancellation)) {
+      CheckGoogExperimentalEchoCancellationDefault(properties, true);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_typing_noise_detection)) {
+      EXPECT_TRUE(properties.goog_typing_noise_detection);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_noise_suppression)) {
+      EXPECT_TRUE(properties.goog_noise_suppression);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_noise_suppression)) {
+      EXPECT_TRUE(properties.goog_experimental_noise_suppression);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_beamforming)) {
+      EXPECT_TRUE(properties.goog_beamforming);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_highpass_filter)) {
+      EXPECT_TRUE(properties.goog_highpass_filter);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_auto_gain_control)) {
+      EXPECT_TRUE(properties.goog_experimental_auto_gain_control);
+    }
+  }
+
+  void CheckBoolDefaultsContentCapture(
+      const AudioSettingsBoolMembers& exclude_main_settings,
+      const AudioPropertiesBoolMembers& exclude_audio_properties,
+      const AudioCaptureSettings& result) {
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::hotword_enabled)) {
+      EXPECT_FALSE(result.hotword_enabled());
+    }
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::disable_local_echo)) {
+      EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
+                result.disable_local_echo());
+    }
+    if (!Contains(exclude_main_settings,
+                  &AudioCaptureSettings::render_to_associated_sink)) {
+      EXPECT_FALSE(result.render_to_associated_sink());
+    }
+
+    const auto& properties = result.audio_processing_properties();
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::enable_sw_echo_cancellation)) {
+      EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::disable_hw_echo_cancellation)) {
+      EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_audio_mirroring)) {
+      EXPECT_FALSE(properties.goog_audio_mirroring);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_auto_gain_control)) {
+      EXPECT_FALSE(properties.goog_auto_gain_control);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_echo_cancellation)) {
+      EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_typing_noise_detection)) {
+      EXPECT_FALSE(properties.goog_typing_noise_detection);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_noise_suppression)) {
+      EXPECT_FALSE(properties.goog_noise_suppression);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_noise_suppression)) {
+      EXPECT_FALSE(properties.goog_experimental_noise_suppression);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_beamforming)) {
+      EXPECT_FALSE(properties.goog_beamforming);
+    }
+    if (!Contains(exclude_audio_properties,
+                  &AudioProcessingProperties::goog_highpass_filter)) {
+      EXPECT_FALSE(properties.goog_highpass_filter);
+    }
+    if (!Contains(
+            exclude_audio_properties,
+            &AudioProcessingProperties::goog_experimental_auto_gain_control)) {
+      EXPECT_FALSE(properties.goog_experimental_auto_gain_control);
+    }
+  }
+
+  void CheckBoolDefaults(
+      const AudioSettingsBoolMembers& exclude_main_settings,
+      const AudioPropertiesBoolMembers& exclude_audio_properties,
+      const AudioCaptureSettings& result) {
+    if (IsDeviceCapture()) {
+      CheckBoolDefaultsDeviceCapture(exclude_main_settings,
+                                     exclude_audio_properties, result);
+    } else {
+      CheckBoolDefaultsContentCapture(exclude_main_settings,
+                                      exclude_audio_properties, result);
+    }
+  }
+
+  void CheckDevice(const mojom::AudioInputDeviceCapabilities& expected_device,
+                   const AudioCaptureSettings& result) {
+    EXPECT_EQ(expected_device.device_id, result.device_id());
+    EXPECT_EQ(expected_device.parameters.sample_rate(),
+              result.device_parameters().sample_rate());
+    EXPECT_EQ(expected_device.parameters.bits_per_sample(),
+              result.device_parameters().bits_per_sample());
+    EXPECT_EQ(expected_device.parameters.channels(),
+              result.device_parameters().channels());
+    EXPECT_EQ(expected_device.parameters.effects(),
+              result.device_parameters().effects());
+  }
+
+  void CheckDeviceDefaults(const AudioCaptureSettings& result) {
+    if (IsDeviceCapture())
+      CheckDevice(*default_device_, result);
+    else
+      EXPECT_TRUE(result.device_id().empty());
+  }
+
+  void CheckGeometryDefaults(const AudioCaptureSettings& result) {
+    EXPECT_TRUE(
+        result.audio_processing_properties().goog_array_geometry.empty());
+  }
+
+  void CheckAllDefaults(
+      const AudioSettingsBoolMembers& exclude_main_settings,
+      const AudioPropertiesBoolMembers& exclude_audio_properties,
+      const AudioCaptureSettings& result) {
+    CheckBoolDefaults(exclude_main_settings, exclude_audio_properties, result);
+    CheckDeviceDefaults(result);
+    CheckGeometryDefaults(result);
+  }
+
+  MockConstraintFactory constraint_factory_;
+  AudioDeviceCaptureCapabilities capabilities_;
+  const mojom::AudioInputDeviceCapabilities* default_device_ = nullptr;
+  const mojom::AudioInputDeviceCapabilities* mono_phone_device_ = nullptr;
+  const mojom::AudioInputDeviceCapabilities* hw_echo_canceller_device_ =
+      nullptr;
+  const mojom::AudioInputDeviceCapabilities* octagonal_device_ = nullptr;
+  const mojom::AudioInputDeviceCapabilities* geometry_device_ = nullptr;
+  const std::vector<media::Point> kMicPositions = {{8, 8, 8}, {4, 4, 4}};
+};
+
+// The Unconstrained test checks the default selection criteria.
+TEST_P(MediaStreamConstraintsUtilAudioTest, Unconstrained) {
+  auto result = SelectSettings();
+
+  // All settings should have default values.
+  EXPECT_TRUE(result.HasValue());
+  CheckAllDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                   result);
+}
+
+// This test checks all possible ways to set boolean constraints (except
+// echo cancellation constraints, which are not mapped 1:1 to output audio
+// processing properties).
+TEST_P(MediaStreamConstraintsUtilAudioTest, SingleBoolConstraint) {
+  const AudioSettingsBoolMembers kMainSettings = {
+      &AudioCaptureSettings::hotword_enabled,
+      &AudioCaptureSettings::disable_local_echo,
+      &AudioCaptureSettings::render_to_associated_sink};
+
+  const std::vector<
+      blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
+      kMainBoolConstraints = {
+          &blink::WebMediaTrackConstraintSet::hotword_enabled,
+          &blink::WebMediaTrackConstraintSet::disable_local_echo,
+          &blink::WebMediaTrackConstraintSet::render_to_associated_sink};
+
+  ASSERT_EQ(kMainSettings.size(), kMainBoolConstraints.size());
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (size_t i = 0; i < kMainSettings.size(); ++i) {
+        for (bool value : kBoolValues) {
+          ResetFactory();
+          (((constraint_factory_.*accessor)().*kMainBoolConstraints[i]).*
+           set_function)(value);
+          auto result = SelectSettings();
+          EXPECT_TRUE(result.HasValue());
+          EXPECT_EQ(value, (result.*kMainSettings[i])());
+          CheckAllDefaults({kMainSettings[i]}, AudioPropertiesBoolMembers(),
+                           result);
+        }
+      }
+    }
+  }
+
+  const AudioPropertiesBoolMembers kAudioProcessingProperties = {
+      &AudioProcessingProperties::goog_audio_mirroring,
+      &AudioProcessingProperties::goog_auto_gain_control,
+      &AudioProcessingProperties::goog_experimental_echo_cancellation,
+      &AudioProcessingProperties::goog_typing_noise_detection,
+      &AudioProcessingProperties::goog_noise_suppression,
+      &AudioProcessingProperties::goog_experimental_noise_suppression,
+      &AudioProcessingProperties::goog_beamforming,
+      &AudioProcessingProperties::goog_highpass_filter,
+      &AudioProcessingProperties::goog_experimental_auto_gain_control};
+
+  const std::vector<
+      blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
+      kAudioProcessingConstraints = {
+          &blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
+          &blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_echo_cancellation,
+          &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
+          &blink::WebMediaTrackConstraintSet::goog_noise_suppression,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_noise_suppression,
+          &blink::WebMediaTrackConstraintSet::goog_beamforming,
+          &blink::WebMediaTrackConstraintSet::goog_highpass_filter,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_auto_gain_control,
+      };
+
+  ASSERT_EQ(kAudioProcessingProperties.size(),
+            kAudioProcessingConstraints.size());
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (size_t i = 0; i < kAudioProcessingProperties.size(); ++i) {
+        for (bool value : kBoolValues) {
+          ResetFactory();
+          (((constraint_factory_.*accessor)().*kAudioProcessingConstraints[i]).*
+           set_function)(value);
+          auto result = SelectSettings();
+          EXPECT_TRUE(result.HasValue());
+          EXPECT_EQ(value, result.audio_processing_properties().*
+                               kAudioProcessingProperties[i]);
+          CheckAllDefaults(AudioSettingsBoolMembers(),
+                           {kAudioProcessingProperties[i]}, result);
+        }
+      }
+    }
+  }
+}
+
+// DeviceID tests.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactArbitraryDeviceID) {
+  const std::string kArbitraryDeviceID = "arbitrary";
+  constraint_factory_.basic().device_id.SetExact(
+      blink::WebString::FromASCII(kArbitraryDeviceID));
+  auto result = SelectSettings();
+  // kArbitraryDeviceID is invalid for device capture, but it is considered
+  // valid for content capture. For content capture, validation of device
+  // capture is performed by the getUserMedia() implementation.
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().device_id.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kArbitraryDeviceID, result.device_id());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+// DeviceID tests check various ways to deal with the device_id constraint.
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealArbitraryDeviceID) {
+  const std::string kArbitraryDeviceID = "arbitrary";
+  constraint_factory_.basic().device_id.SetIdeal(
+      blink::WebString::FromASCII(kArbitraryDeviceID));
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // kArbitraryDeviceID is invalid for device capture, but it is considered
+  // valid for content capture. For content capture, validation of device
+  // capture is performed by the getUserMedia() implementation.
+  if (IsDeviceCapture())
+    CheckDeviceDefaults(result);
+  else
+    EXPECT_EQ(kArbitraryDeviceID, result.device_id());
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidDeviceID) {
+  for (const auto& device : capabilities_) {
+    constraint_factory_.basic().device_id.SetExact(
+        blink::WebString::FromASCII(device->device_id));
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    CheckDevice(*device, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(),
+                      {&AudioProcessingProperties::enable_sw_echo_cancellation},
+                      result);
+    bool has_hw_echo_cancellation =
+        device->parameters.effects() & media::AudioParameters::ECHO_CANCELLER;
+    EXPECT_EQ(!has_hw_echo_cancellation,
+              result.audio_processing_properties().enable_sw_echo_cancellation);
+    if (&*device == geometry_device_) {
+      EXPECT_EQ(kMicPositions,
+                result.audio_processing_properties().goog_array_geometry);
+    } else {
+      CheckGeometryDefaults(result);
+    }
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidSampleRate) {
+  constraint_factory_.basic().sample_rate.SetExact(
+      media::AudioParameters::kTelephoneSampleRate);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    CheckDevice(*mono_phone_device_, result);
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// SampleRate, SampleSize, ChannelCount tests check that numeric device-related
+// constraints are handled correctly.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidSampleRate) {
+  constraint_factory_.basic().sample_rate.SetExact(666);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().sample_rate.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidSampleRate) {
+  constraint_factory_.basic().sample_rate.SetMin(80000);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one with a large-enough sample rate.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidSampleRate) {
+  constraint_factory_.basic().sample_rate.SetMax(10000);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The mono device is the only one with a small-enough sample rate.
+    CheckDevice(*mono_phone_device_, result);
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidSampleRate) {
+  constraint_factory_.basic().sample_rate.SetMin(1000);
+  constraint_factory_.basic().sample_rate.SetMax(10000);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The mono device is the only one with a sample rate in the range.
+    CheckDevice(*mono_phone_device_, result);
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeSampleRate) {
+  constraint_factory_.basic().sample_rate.SetMin(9000);
+  constraint_factory_.basic().sample_rate.SetMax(10000);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().sample_rate.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealSampleRate) {
+  constraint_factory_.basic().sample_rate.SetIdeal(10000);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The mono device is the one a sample rate closest to ideal.
+    CheckDevice(*mono_phone_device_, result);
+  } else {
+    // Content capture ignores the sample_rate constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// Sample size tests.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidSampleSize) {
+  constraint_factory_.basic().sample_size.SetExact(8);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidSampleSize) {
+  constraint_factory_.basic().sample_size.SetExact(666);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().sample_size.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidSampleSize) {
+  constraint_factory_.basic().sample_size.SetMin(20);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The device with echo canceller is the only one with a sample size that
+    // is greater than the requested minimum.
+    CheckDevice(*hw_echo_canceller_device_, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(),
+                      {&AudioProcessingProperties::enable_sw_echo_cancellation},
+                      result);
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+  }
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidSampleSize) {
+  constraint_factory_.basic().sample_size.SetMax(10);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one with a small-enough sample size.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidSampleSize) {
+  constraint_factory_.basic().sample_size.SetMin(3);
+  constraint_factory_.basic().sample_size.SetMax(15);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one with a sample size in the range.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeSampleSize) {
+  constraint_factory_.basic().sample_size.SetMin(10);
+  constraint_factory_.basic().sample_size.SetMax(15);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().sample_size.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealSampleSize) {
+  constraint_factory_.basic().sample_size.SetIdeal(10);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the one a sample size closest to ideal.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the sample_size constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// ChannelCount tests.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidChannelCount) {
+  constraint_factory_.basic().channel_count.SetExact(8);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidChannelCount) {
+  constraint_factory_.basic().channel_count.SetExact(666);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().channel_count.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidChannelCount) {
+  constraint_factory_.basic().channel_count.SetMin(7);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The device with echo canceller is the only one with a channel count that
+    // is greater than the requested minimum.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidChannelCount) {
+  constraint_factory_.basic().channel_count.SetMax(1);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one with a small-enough channel count.
+    CheckDevice(*mono_phone_device_, result);
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidChannelCount) {
+  constraint_factory_.basic().channel_count.SetMin(3);
+  constraint_factory_.basic().channel_count.SetMax(15);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one with a channel count in the range.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeChannelCount) {
+  constraint_factory_.basic().channel_count.SetMin(3);
+  constraint_factory_.basic().channel_count.SetMax(7);
+  auto result = SelectSettings();
+  if (IsDeviceCapture()) {
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(std::string(constraint_factory_.basic().channel_count.GetName()),
+              std::string(result.failed_constraint_name()));
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_TRUE(result.device_id().empty());
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    CheckGeometryDefaults(result);
+  }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealChannelCount) {
+  constraint_factory_.basic().channel_count.SetIdeal(6);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the one the number of channels closest to ideal.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores the channel_count constraint.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// Tests the echoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithSw) {
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (bool value : kBoolValues) {
+        ResetFactory();
+        ((constraint_factory_.*accessor)().echo_cancellation.*
+         set_function)(value);
+        auto result = SelectSettings();
+        EXPECT_TRUE(result.HasValue());
+        const AudioProcessingProperties& properties =
+            result.audio_processing_properties();
+        // With device capture, the echo_cancellation constraint
+        // enables/disables all audio processing by default.
+        // With content capture, the echo_cancellation constraint controls
+        // only the echo_cancellation properties. The other audio processing
+        // properties default to false.
+        bool enable_sw_audio_processing = IsDeviceCapture() ? value : false;
+        EXPECT_EQ(value, properties.enable_sw_echo_cancellation);
+        EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+        EXPECT_EQ(enable_sw_audio_processing,
+                  properties.goog_auto_gain_control);
+        CheckGoogExperimentalEchoCancellationDefault(
+            properties, enable_sw_audio_processing);
+        EXPECT_EQ(enable_sw_audio_processing,
+                  properties.goog_typing_noise_detection);
+        EXPECT_EQ(enable_sw_audio_processing,
+                  properties.goog_noise_suppression);
+        EXPECT_EQ(enable_sw_audio_processing,
+                  properties.goog_experimental_noise_suppression);
+        EXPECT_EQ(enable_sw_audio_processing, properties.goog_beamforming);
+        EXPECT_EQ(enable_sw_audio_processing, properties.goog_highpass_filter);
+        EXPECT_EQ(enable_sw_audio_processing,
+                  properties.goog_experimental_auto_gain_control);
+
+        // The following are not audio processing.
+        EXPECT_FALSE(properties.goog_audio_mirroring);
+        EXPECT_FALSE(result.hotword_enabled());
+        EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
+                  result.disable_local_echo());
+        EXPECT_FALSE(result.render_to_associated_sink());
+        if (IsDeviceCapture()) {
+          CheckDevice(*default_device_, result);
+        } else {
+          EXPECT_TRUE(result.device_id().empty());
+        }
+      }
+    }
+  }
+}
+
+// Tests the echoCancellation constraint with a device with hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithHw) {
+  // With content capture, there is no hardware echo cancellation, so
+  // nothing to test.
+  if (!IsDeviceCapture())
+    return;
+
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (bool value : kBoolValues) {
+        ResetFactory();
+        constraint_factory_.basic().device_id.SetExact(
+            blink::WebString::FromASCII(hw_echo_canceller_device_->device_id));
+        ((constraint_factory_.*accessor)().echo_cancellation.*
+         set_function)(value);
+        auto result = SelectSettings();
+        EXPECT_TRUE(result.HasValue());
+        const AudioProcessingProperties& properties =
+            result.audio_processing_properties();
+        // With hardware echo cancellation, the echo_cancellation constraint
+        // enables/disables all audio processing by default, software echo
+        // cancellation is always disabled, and hardware echo cancellation is
+        // disabled if the echo_cancellation constraint is false.
+        EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+        EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+        EXPECT_EQ(value, properties.goog_auto_gain_control);
+        CheckGoogExperimentalEchoCancellationDefault(properties, value);
+        EXPECT_EQ(value, properties.goog_typing_noise_detection);
+        EXPECT_EQ(value, properties.goog_noise_suppression);
+        EXPECT_EQ(value, properties.goog_experimental_noise_suppression);
+        EXPECT_EQ(value, properties.goog_beamforming);
+        EXPECT_EQ(value, properties.goog_highpass_filter);
+        EXPECT_EQ(value, properties.goog_experimental_auto_gain_control);
+
+        // The following are not audio processing.
+        EXPECT_FALSE(properties.goog_audio_mirroring);
+        EXPECT_FALSE(result.hotword_enabled());
+        EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
+                  result.disable_local_echo());
+        EXPECT_FALSE(result.render_to_associated_sink());
+        CheckDevice(*hw_echo_canceller_device_, result);
+      }
+    }
+  }
+}
+
+// Tests the googEchoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithSw) {
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (bool value : kBoolValues) {
+        ResetFactory();
+        ((constraint_factory_.*accessor)().goog_echo_cancellation.*
+         set_function)(value);
+        auto result = SelectSettings();
+        EXPECT_TRUE(result.HasValue());
+        const AudioProcessingProperties& properties =
+            result.audio_processing_properties();
+        // The goog_echo_cancellation constraint controls only the
+        // echo_cancellation properties. The other audio processing properties
+        // use the default values.
+        EXPECT_EQ(value, properties.enable_sw_echo_cancellation);
+        EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+        CheckBoolDefaults(
+            AudioSettingsBoolMembers(),
+            {
+                &AudioProcessingProperties::enable_sw_echo_cancellation,
+                &AudioProcessingProperties::disable_hw_echo_cancellation,
+            },
+            result);
+        if (IsDeviceCapture()) {
+          CheckDevice(*default_device_, result);
+        } else {
+          EXPECT_TRUE(result.device_id().empty());
+        }
+      }
+    }
+  }
+}
+
+// Tests the googEchoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithHw) {
+  // With content capture, there is no hardware echo cancellation, so
+  // nothing to test.
+  if (!IsDeviceCapture())
+    return;
+
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (bool value : kBoolValues) {
+        ResetFactory();
+        constraint_factory_.basic().device_id.SetExact(
+            blink::WebString::FromASCII(hw_echo_canceller_device_->device_id));
+        ((constraint_factory_.*accessor)().goog_echo_cancellation.*
+         set_function)(value);
+        auto result = SelectSettings();
+        EXPECT_TRUE(result.HasValue());
+        const AudioProcessingProperties& properties =
+            result.audio_processing_properties();
+        // With hardware echo cancellation, software echo cancellation is always
+        // disabled, and hardware echo cancellation is disabled if
+        // goog_echo_cancellation is false.
+        EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+        EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+        CheckBoolDefaults(
+            AudioSettingsBoolMembers(),
+            {
+                &AudioProcessingProperties::enable_sw_echo_cancellation,
+                &AudioProcessingProperties::disable_hw_echo_cancellation,
+            },
+            result);
+        CheckDevice(*hw_echo_canceller_device_, result);
+      }
+    }
+  }
+}
+
+// Test that having differing mandatory values for echoCancellation and
+// googEchoCancellation fails.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ContradictoryEchoCancellation) {
+  for (bool value : kBoolValues) {
+    constraint_factory_.basic().echo_cancellation.SetExact(value);
+    constraint_factory_.basic().goog_echo_cancellation.SetExact(!value);
+    auto result = SelectSettings();
+    EXPECT_FALSE(result.HasValue());
+    EXPECT_EQ(result.failed_constraint_name(),
+              constraint_factory_.basic().echo_cancellation.GetName());
+  }
+}
+
+// Tests that individual boolean audio-processing constraints override the
+// default value set by the echoCancellation constraint.
+TEST_P(MediaStreamConstraintsUtilAudioTest,
+       EchoCancellationAndSingleBoolConstraint) {
+  const AudioPropertiesBoolMembers kAudioProcessingProperties = {
+      &AudioProcessingProperties::goog_audio_mirroring,
+      &AudioProcessingProperties::goog_auto_gain_control,
+      &AudioProcessingProperties::goog_experimental_echo_cancellation,
+      &AudioProcessingProperties::goog_typing_noise_detection,
+      &AudioProcessingProperties::goog_noise_suppression,
+      &AudioProcessingProperties::goog_experimental_noise_suppression,
+      &AudioProcessingProperties::goog_beamforming,
+      &AudioProcessingProperties::goog_highpass_filter,
+      &AudioProcessingProperties::goog_experimental_auto_gain_control};
+
+  const std::vector<
+      blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
+      kAudioProcessingConstraints = {
+          &blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
+          &blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_echo_cancellation,
+          &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
+          &blink::WebMediaTrackConstraintSet::goog_noise_suppression,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_noise_suppression,
+          &blink::WebMediaTrackConstraintSet::goog_beamforming,
+          &blink::WebMediaTrackConstraintSet::goog_highpass_filter,
+          &blink::WebMediaTrackConstraintSet::
+              goog_experimental_auto_gain_control,
+      };
+
+  ASSERT_EQ(kAudioProcessingProperties.size(),
+            kAudioProcessingConstraints.size());
+  for (auto set_function : kBoolSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == &blink::BooleanConstraint::SetIdeal &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      for (size_t i = 0; i < kAudioProcessingProperties.size(); ++i) {
+        ResetFactory();
+        ((constraint_factory_.*accessor)().echo_cancellation.*
+         set_function)(false);
+        (((constraint_factory_.*accessor)().*kAudioProcessingConstraints[i]).*
+         set_function)(true);
+        auto result = SelectSettings();
+        EXPECT_TRUE(result.HasValue());
+        EXPECT_FALSE(
+            result.audio_processing_properties().enable_sw_echo_cancellation);
+        EXPECT_TRUE(
+            result.audio_processing_properties().disable_hw_echo_cancellation);
+        EXPECT_TRUE(result.audio_processing_properties().*
+                    kAudioProcessingProperties[i]);
+        for (size_t j = 0; j < kAudioProcessingProperties.size(); ++j) {
+          if (i == j)
+            continue;
+          EXPECT_FALSE(result.audio_processing_properties().*
+                       kAudioProcessingProperties[j]);
+        }
+      }
+    }
+  }
+}
+
+// Test advanced constraints sets that can be satisfied.
+TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedCompatibleConstraints) {
+  constraint_factory_.AddAdvanced().sample_size.SetExact(8);
+  constraint_factory_.AddAdvanced().render_to_associated_sink.SetExact(true);
+  constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one that matches the first advanced
+    // constraint set.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores device-related constraints.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults({&AudioCaptureSettings::render_to_associated_sink},
+                    {&AudioProcessingProperties::goog_audio_mirroring}, result);
+  CheckGeometryDefaults(result);
+  EXPECT_TRUE(result.render_to_associated_sink());
+  EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
+}
+
+// Test that an advanced constraint set that cannot be satisfied is ignored.
+TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedSelfConflictingConstraint) {
+  auto& advanced = constraint_factory_.AddAdvanced();
+  advanced.sample_size.SetExact(8);
+  advanced.sample_rate.SetExact(8);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  // The advanced constraint is self conflicting and ignored. The default
+  // device is selected.
+  CheckDeviceDefaults(result);
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// Test that an advanced constraint set that contradicts a previous constraint
+// set with a device-related constraint is ignored.
+TEST_P(MediaStreamConstraintsUtilAudioTest,
+       AdvancedConflictingDeviceConstraint) {
+  constraint_factory_.AddAdvanced().sample_size.SetExact(8);
+  constraint_factory_.AddAdvanced().sample_size.SetExact(
+      media::AudioParameters::kAudioCDSampleRate);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one that matches the first advanced
+    // constraint set. The second set is ignored.
+    CheckDevice(*octagonal_device_, result);
+    EXPECT_NE(media::AudioParameters::kAudioCDSampleRate,
+              result.device_parameters().sample_rate());
+  } else {
+    // Content capture ignores device-related constraints.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                    result);
+  CheckGeometryDefaults(result);
+}
+
+// Test that an advanced constraint set that contradicts a previous constraint
+// set is ignored, but that further constraint sets that can be satisfied are
+// applied.
+TEST_P(MediaStreamConstraintsUtilAudioTest,
+       AdvancedConflictingMiddleConstraints) {
+  constraint_factory_.AddAdvanced().sample_size.SetExact(8);
+  auto& advanced2 = constraint_factory_.AddAdvanced();
+  advanced2.sample_rate.SetExact(123456);
+  advanced2.hotword_enabled.SetExact(true);
+  constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one that matches the first advanced
+    // constraint set.
+    CheckDevice(*octagonal_device_, result);
+    // Second advanced set is discarded because no device has the requested
+    // sample rate.
+    EXPECT_NE(123456, result.device_parameters().sample_rate());
+    EXPECT_FALSE(result.hotword_enabled());
+  } else {
+    // Content capture ignores device-related constraints. Thus, it does not
+    // discard the second advanced set.
+    EXPECT_TRUE(result.device_id().empty());
+    EXPECT_TRUE(result.hotword_enabled());
+  }
+  CheckBoolDefaults({&AudioCaptureSettings::render_to_associated_sink,
+                     &AudioCaptureSettings::hotword_enabled},
+                    {&AudioProcessingProperties::goog_audio_mirroring}, result);
+  CheckGeometryDefaults(result);
+  EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
+}
+
+// Test that an advanced constraint set that contradicts a previous constraint
+// set with a boolean constraint is ignored.
+TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedConflictingLastConstraint) {
+  constraint_factory_.AddAdvanced().sample_size.SetExact(8);
+  constraint_factory_.AddAdvanced().hotword_enabled.SetExact(true);
+  constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
+  constraint_factory_.AddAdvanced().hotword_enabled.SetExact(false);
+  auto result = SelectSettings();
+  EXPECT_TRUE(result.HasValue());
+  if (IsDeviceCapture()) {
+    // The octagonal device is the only one that matches the first advanced
+    // constraint set.
+    CheckDevice(*octagonal_device_, result);
+  } else {
+    // Content capture ignores device-related constraints.
+    EXPECT_TRUE(result.device_id().empty());
+  }
+  CheckBoolDefaults({&AudioCaptureSettings::hotword_enabled},
+                    {&AudioProcessingProperties::goog_audio_mirroring}, result);
+  CheckGeometryDefaults(result);
+  // The fourth advanced set is ignored because it contradicts the second set.
+  EXPECT_TRUE(result.hotword_enabled());
+  EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
+}
+
+// Test that a valid geometry is interpreted correctly in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ValidGeometry) {
+  const blink::WebString kGeometry =
+      blink::WebString::FromASCII("-0.02 0 0 0.02 0 1.01");
+
+  for (auto set_function : kStringSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      // Using kStringSetFunctions[1] instead of
+      // static_cast<StringSetFunction>(&blink::StringConstraint::SetIdeal)
+      // because the equality comparison provides the wrong result in the
+      // Windows Debug build, making the test fail.
+      if (set_function == kStringSetFunctions[1] &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      ResetFactory();
+      ((constraint_factory_.*accessor)().goog_array_geometry.*
+       set_function)(kGeometry);
+      auto result = SelectSettings();
+      EXPECT_TRUE(result.HasValue());
+      CheckDeviceDefaults(result);
+      CheckBoolDefaults(AudioSettingsBoolMembers(),
+                        AudioPropertiesBoolMembers(), result);
+      const std::vector<media::Point>& geometry =
+          result.audio_processing_properties().goog_array_geometry;
+      EXPECT_EQ(2u, geometry.size());
+      EXPECT_EQ(media::Point(-0.02, 0, 0), geometry[0]);
+      EXPECT_EQ(media::Point(0.02, 0, 1.01), geometry[1]);
+    }
+  }
+}
+
+// Test that an invalid geometry is interpreted as empty in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidGeometry) {
+  const blink::WebString kGeometry =
+      blink::WebString::FromASCII("1 1 1 invalid");
+
+  for (auto set_function : kStringSetFunctions) {
+    for (auto accessor : kFactoryAccessors) {
+      // Ideal advanced is ignored by the SelectSettings algorithm.
+      if (set_function == static_cast<StringSetFunction>(
+                              &blink::StringConstraint::SetIdeal) &&
+          accessor == &MockConstraintFactory::AddAdvanced) {
+        continue;
+      }
+      ResetFactory();
+      ((constraint_factory_.*accessor)().goog_array_geometry.*
+       set_function)(kGeometry);
+      auto result = SelectSettings();
+      EXPECT_TRUE(result.HasValue());
+      CheckDeviceDefaults(result);
+      CheckBoolDefaults(AudioSettingsBoolMembers(),
+                        AudioPropertiesBoolMembers(), result);
+      const std::vector<media::Point>& geometry =
+          result.audio_processing_properties().goog_array_geometry;
+      EXPECT_EQ(0u, geometry.size());
+    }
+  }
+}
+
+// Test that an invalid geometry is interpreted as empty in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, DeviceGeometry) {
+  if (!IsDeviceCapture())
+    return;
+
+  constraint_factory_.basic().device_id.SetExact(
+      blink::WebString::FromASCII(geometry_device_->device_id));
+
+  {
+    const blink::WebString kValidGeometry =
+        blink::WebString::FromASCII("-0.02 0 0  0.02 0 1.01");
+    constraint_factory_.basic().goog_array_geometry.SetExact(kValidGeometry);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    CheckDevice(*geometry_device_, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    // Constraints geometry should be preferred over device geometry.
+    const std::vector<media::Point>& geometry =
+        result.audio_processing_properties().goog_array_geometry;
+    EXPECT_EQ(2u, geometry.size());
+    EXPECT_EQ(media::Point(-0.02, 0, 0), geometry[0]);
+    EXPECT_EQ(media::Point(0.02, 0, 1.01), geometry[1]);
+  }
+
+  {
+    constraint_factory_.basic().goog_array_geometry.SetExact(
+        blink::WebString());
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    CheckDevice(*geometry_device_, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    // Empty geometry is valid and should be preferred over device geometry.
+    EXPECT_TRUE(
+        result.audio_processing_properties().goog_array_geometry.empty());
+  }
+
+  {
+    const blink::WebString kInvalidGeometry =
+        blink::WebString::FromASCII("1 1 1 invalid");
+    constraint_factory_.basic().goog_array_geometry.SetExact(kInvalidGeometry);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    CheckDevice(*geometry_device_, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+                      result);
+    // Device geometry should be preferred over invalid constraints geometry.
+    EXPECT_EQ(kMicPositions,
+              result.audio_processing_properties().goog_array_geometry);
+  }
+}
+
+// NoDevices tests verify that the case with no devices is handled correctly.
+TEST_P(MediaStreamConstraintsUtilAudioTest, NoDevicesNoConstraints) {
+  // This test makes sense only for device capture.
+  if (!IsDeviceCapture())
+    return;
+
+  AudioDeviceCaptureCapabilities capabilities;
+  auto result = SelectSettingsAudioCapture(
+      capabilities, constraint_factory_.CreateWebMediaConstraints());
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, NoDevicesWithConstraints) {
+  // This test makes sense only for device capture.
+  if (!IsDeviceCapture())
+    return;
+
+  AudioDeviceCaptureCapabilities capabilities;
+  constraint_factory_.basic().sample_size.SetExact(16);
+  auto result = SelectSettingsAudioCapture(
+      capabilities, constraint_factory_.CreateWebMediaConstraints());
+  EXPECT_FALSE(result.HasValue());
+  EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        MediaStreamConstraintsUtilAudioTest,
+                        testing::Values("",
+                                        kMediaStreamSourceTab,
+                                        kMediaStreamSourceSystem,
+                                        kMediaStreamSourceDesktop));
+
+}  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util_sets.cc b/content/renderer/media/media_stream_constraints_util_sets.cc
index 66deebf5..a6671972 100644
--- a/content/renderer/media/media_stream_constraints_util_sets.cc
+++ b/content/renderer/media/media_stream_constraints_util_sets.cc
@@ -533,4 +533,24 @@
       MaxAspectRatioFromConstraint(constraint_set.aspect_ratio));
 }
 
+DiscreteSet<std::string> StringSetFromConstraint(
+    const blink::StringConstraint& constraint) {
+  if (!constraint.HasExact())
+    return DiscreteSet<std::string>::UniversalSet();
+
+  std::vector<std::string> elements;
+  for (const auto& entry : constraint.Exact())
+    elements.push_back(entry.Ascii());
+
+  return DiscreteSet<std::string>(std::move(elements));
+}
+
+DiscreteSet<bool> BoolSetFromConstraint(
+    const blink::BooleanConstraint& constraint) {
+  if (!constraint.HasExact())
+    return DiscreteSet<bool>::UniversalSet();
+
+  return DiscreteSet<bool>({constraint.Exact()});
+}
+
 }  // namespace content
diff --git a/content/renderer/media/media_stream_constraints_util_sets.h b/content/renderer/media/media_stream_constraints_util_sets.h
index 0cc6ab9..053db68 100644
--- a/content/renderer/media/media_stream_constraints_util_sets.h
+++ b/content/renderer/media/media_stream_constraints_util_sets.h
@@ -7,6 +7,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -71,9 +72,12 @@
 // is application defined (e.g., it could be all possible boolean values, all
 // possible strings of length N, or anything that suits a particular
 // application).
+// TODO(guidou): Rename this class. http://crbug.com/731166
 template <typename T>
 class DiscreteSet {
  public:
+  // Creates a universal set.
+  DiscreteSet() : is_universal_(true) {}
   // Creates a set containing the elements in |elements|.
   // It is the responsibility of the caller to ensure that |elements| is not
   // equivalent to the universal set and that |elements| has no repeated
@@ -127,16 +131,25 @@
     return elements_[0];
   }
 
+  // Returns a reference to the list of elements in the set.
+  // Behavior is undefined if the set is universal.
+  const std::vector<T>& elements() const {
+    DCHECK(!is_universal_);
+    return elements_;
+  }
+
   bool is_universal() const { return is_universal_; }
 
  private:
-  // Creates a universal set.
-  DiscreteSet() : is_universal_(true) {}
-
   bool is_universal_;
   std::vector<T> elements_;
 };
 
+DiscreteSet<std::string> StringSetFromConstraint(
+    const blink::StringConstraint& constraint);
+DiscreteSet<bool> BoolSetFromConstraint(
+    const blink::BooleanConstraint& constraint);
+
 // This class represents a set of (height, width) screen resolution candidates
 // determined by width, height and aspect-ratio constraints.
 // This class supports widths and heights from 0 to kMaxDimension, both
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.cc b/content/renderer/media/media_stream_constraints_util_video_content.cc
index 82f447f..6eac825 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_content.cc
@@ -45,7 +45,6 @@
 using StringSet = DiscreteSet<std::string>;
 using BoolSet = DiscreteSet<bool>;
 
-
 constexpr double kMinScreenCastAspectRatio =
     static_cast<double>(kMinScreenCastDimension) /
     static_cast<double>(kMaxScreenCastDimension);
@@ -53,24 +52,6 @@
     static_cast<double>(kMaxScreenCastDimension) /
     static_cast<double>(kMinScreenCastDimension);
 
-StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
-  if (!constraint.HasExact())
-    return StringSet::UniversalSet();
-
-  std::vector<std::string> elements;
-  for (const auto& entry : constraint.Exact())
-    elements.push_back(entry.Ascii());
-
-  return StringSet(std::move(elements));
-}
-
-BoolSet BoolSetFromConstraint(const blink::BooleanConstraint& constraint) {
-  if (!constraint.HasExact())
-    return BoolSet::UniversalSet();
-
-  return BoolSet({constraint.Exact()});
-}
-
 using DoubleRangeSet = NumericRangeSet<double>;
 
 class VideoContentCaptureCandidates {
@@ -79,9 +60,7 @@
       : has_explicit_max_height_(false),
         has_explicit_max_width_(false),
         has_explicit_min_frame_rate_(false),
-        has_explicit_max_frame_rate_(false),
-        device_id_set_(StringSet::UniversalSet()),
-        noise_reduction_set_(BoolSet::UniversalSet()) {}
+        has_explicit_max_frame_rate_(false) {}
   explicit VideoContentCaptureCandidates(
       const blink::WebMediaTrackConstraintSet& constraint_set)
       : resolution_set_(ResolutionSet::FromConstraintSet(constraint_set)),
diff --git a/content/renderer/media/media_stream_constraints_util_video_device.cc b/content/renderer/media/media_stream_constraints_util_video_device.cc
index f2c361f9..db62a0a 100644
--- a/content/renderer/media/media_stream_constraints_util_video_device.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_device.cc
@@ -189,17 +189,6 @@
       constrained_format.constrained_frame_rate().Max());
 }
 
-// Generic distance function between two numeric values. Based on the fitness
-// distance function described in
-// https://w3c.github.io/mediacapture-main/#dfn-fitness-distance
-double Distance(double value1, double value2) {
-  if (std::fabs(value1 - value2) <= blink::DoubleConstraint::kConstraintEpsilon)
-    return 0.0;
-
-  return std::fabs(value1 - value2) /
-         std::max(std::fabs(value1), std::fabs(value2));
-}
-
 // Returns a pair with the minimum and maximum aspect ratios supported by the
 // candidate format |constrained_format|, subject to given width and height
 // constraints.
@@ -284,7 +273,8 @@
 
   // If the source value exceeds the maximum requested, penalize.
   if (constraint_has_max && native_source_value > constraint_max)
-    return Distance(native_source_value, constraint_max);
+    return NumericConstraintFitnessDistance(native_source_value,
+                                            constraint_max);
 
   return 0.0;
 }
@@ -326,7 +316,8 @@
   // Compute the cost using the native rate.
   if (constraint_has_max &&
       constrained_format.native_frame_rate() > constraint_max)
-    return Distance(constrained_format.native_frame_rate(), constraint_max);
+    return NumericConstraintFitnessDistance(
+        constrained_format.native_frame_rate(), constraint_max);
 
   return 0.0;
 }
@@ -500,22 +491,6 @@
              failed_constraint_name);
 }
 
-// Returns the fitness distance between |value| and |constraint|.
-// Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
-double StringConstraintFitnessDistance(
-    const blink::WebString& value,
-    const blink::StringConstraint& constraint) {
-  if (!constraint.HasIdeal())
-    return 0.0;
-
-  for (auto& ideal_value : constraint.Ideal()) {
-    if (value == ideal_value)
-      return 0.0;
-  }
-
-  return 1.0;
-}
-
 // Returns the fitness distance between |value| and |constraint| for
 // resolution constraints (i.e., width and height).
 // Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
@@ -530,7 +505,7 @@
   if (value >= constraint.Ideal())
     return 0.0;
 
-  return Distance(value, constraint.Ideal());
+  return NumericConstraintFitnessDistance(value, constraint.Ideal());
 }
 
 // Returns the fitness distance between |value| and |constraint| for
@@ -540,7 +515,9 @@
 double ResolutionConstraintNativeFitnessDistance(
     long value,
     const blink::LongConstraint& constraint) {
-  return constraint.HasIdeal() ? Distance(value, constraint.Ideal()) : 0.0;
+  return constraint.HasIdeal()
+             ? NumericConstraintFitnessDistance(value, constraint.Ideal())
+             : 0.0;
 }
 
 // Returns the fitness distance between a source resolution settings
@@ -566,13 +543,15 @@
   if (max_source_aspect_ratio <
       aspect_ratio_constraint.Ideal() -
           blink::DoubleConstraint::kConstraintEpsilon) {
-    return Distance(max_source_aspect_ratio, aspect_ratio_constraint.Ideal());
+    return NumericConstraintFitnessDistance(max_source_aspect_ratio,
+                                            aspect_ratio_constraint.Ideal());
   }
 
   if (min_source_aspect_ratio >
       aspect_ratio_constraint.Ideal() +
           blink::DoubleConstraint::kConstraintEpsilon) {
-    return Distance(min_source_aspect_ratio, aspect_ratio_constraint.Ideal());
+    return NumericConstraintFitnessDistance(min_source_aspect_ratio,
+                                            aspect_ratio_constraint.Ideal());
   }
 
   // Otherwise, the ideal aspect ratio can be supported and the fitness is 0.
@@ -595,7 +574,7 @@
     return 0.0;
   }
 
-  return Distance(value, constraint.Ideal());
+  return NumericConstraintFitnessDistance(value, constraint.Ideal());
 }
 
 // Returns the fitness distance between |value| and |constraint| for the
@@ -605,7 +584,9 @@
 double FrameRateConstraintNativeFitnessDistance(
     double value,
     const blink::DoubleConstraint& constraint) {
-  return constraint.HasIdeal() ? Distance(value, constraint.Ideal()) : 0.0;
+  return constraint.HasIdeal()
+             ? NumericConstraintFitnessDistance(value, constraint.Ideal())
+             : 0.0;
 }
 
 // Returns the fitness distance between |value| and |constraint| for the
@@ -735,15 +716,17 @@
   double resolution_distance =
       candidate_area == kDefaultResolutionArea
           ? 0.0
-          : Distance(candidate_area, kDefaultResolutionArea);
+          : NumericConstraintFitnessDistance(candidate_area,
+                                             kDefaultResolutionArea);
   distance_vector->push_back(resolution_distance);
 
   // Prefer a frame rate close to the default.
   double frame_rate_distance =
       candidate.format().frame_rate == MediaStreamVideoSource::kDefaultFrameRate
           ? 0.0
-          : Distance(candidate.format().frame_rate,
-                     MediaStreamVideoSource::kDefaultFrameRate);
+          : NumericConstraintFitnessDistance(
+                candidate.format().frame_rate,
+                MediaStreamVideoSource::kDefaultFrameRate);
   distance_vector->push_back(frame_rate_distance);
 }
 
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index 5c60e6a..7a9bb970 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -104,6 +104,12 @@
   ScheduleUpdateTask();
 }
 
+void RendererWebMediaPlayerDelegate::DidPlayerMutedStatusChange(int delegate_id,
+                                                                bool muted) {
+  Send(new MediaPlayerDelegateHostMsg_OnMutedStatusChanged(routing_id(),
+                                                           delegate_id, muted));
+}
+
 void RendererWebMediaPlayerDelegate::DidPause(int player_id) {
   DVLOG(2) << __func__ << "(" << player_id << ")";
   DCHECK(id_map_.Lookup(player_id));
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h
index e2f9f00b..17b6935 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.h
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -59,6 +59,7 @@
   bool IsStale(int player_id) override;
   void SetIsEffectivelyFullscreen(int player_id, bool is_fullscreen) override;
   void DidPlayerSizeChange(int delegate_id, const gfx::Size& size) override;
+  void DidPlayerMutedStatusChange(int delegate_id, bool muted) override;
 
   // content::RenderFrameObserver overrides.
   void WasHidden() override;
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
index 7ad468d..4d0c71f 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
@@ -169,6 +169,21 @@
     EXPECT_EQ(16, std::get<1>(result).width());
     EXPECT_EQ(9, std::get<1>(result).height());
   }
+
+  // Verify the muted status message.
+  {
+    test_sink().ClearMessages();
+    delegate_manager_->DidPlayerMutedStatusChange(delegate_id, true);
+    const IPC::Message* msg = test_sink().GetUniqueMessageMatching(
+        MediaPlayerDelegateHostMsg_OnMutedStatusChanged::ID);
+    ASSERT_TRUE(msg);
+
+    std::tuple<int, bool> result;
+    ASSERT_TRUE(
+        MediaPlayerDelegateHostMsg_OnMutedStatusChanged::Read(msg, &result));
+    EXPECT_EQ(delegate_id, std::get<0>(result));
+    EXPECT_TRUE(std::get<1>(result));
+  }
 }
 
 TEST_F(RendererWebMediaPlayerDelegateTest, DeliversObserverNotifications) {
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index b4af48649..e7be5e048 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -345,6 +345,7 @@
   volume_ = volume;
   if (audio_renderer_.get())
     audio_renderer_->SetVolume(volume_ * volume_multiplier_);
+  delegate_->DidPlayerMutedStatusChange(delegate_id_, volume == 0.0);
 }
 
 void WebMediaPlayerMS::SetSinkId(
diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc
index 3cb79ca..2afbe16 100644
--- a/content/renderer/media/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/webmediaplayer_ms_unittest.cc
@@ -64,6 +64,10 @@
     is_gone_ = false;
   }
 
+  void DidPlayerMutedStatusChange(int delegate_id, bool muted) override {
+    EXPECT_EQ(delegate_id_, delegate_id);
+  }
+
   void DidPause(int delegate_id) override {
     EXPECT_EQ(delegate_id_, delegate_id);
     EXPECT_TRUE(playing_);
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 2ad3c12..19346c4 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -27,6 +27,7 @@
 #include "base/task_scheduler/initialization_util.h"
 #include "base/time/time.h"
 #include "content/child/site_isolation_stats_gatherer.h"
+#include "content/common/task_scheduler.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
@@ -92,8 +93,9 @@
                                       kSuggestedReclaimTime),
       base::SchedulerWorkerPoolParams(
           StandbyThreadPolicy::LAZY,
-          std::max(kMaxNumThreadsInForegroundPoolLowerBound,
-                   base::SysInfo::NumberOfProcessors()),
+          std::max(
+              kMaxNumThreadsInForegroundPoolLowerBound,
+              content::GetMinThreadsInRendererTaskSchedulerForegroundPool()),
           kSuggestedReclaimTime),
       base::SchedulerWorkerPoolParams(StandbyThreadPolicy::LAZY,
                                       kMaxNumThreadsInForegroundBlockingPool,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4027e5722..da5e62a6 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1094,6 +1094,7 @@
     "../browser/bluetooth/bluetooth_device_chooser_controller_unittest.cc",
     "../browser/bluetooth/frame_connected_bluetooth_devices_unittest.cc",
     "../browser/browser_associated_interface_unittest.cc",
+    "../browser/browser_main_loop_unittest.cc",
     "../browser/browser_thread_unittest.cc",
     "../browser/browser_url_handler_impl_unittest.cc",
     "../browser/browsing_data/browsing_data_filter_builder_impl_unittest.cc",
@@ -1637,6 +1638,7 @@
       "../renderer/media/media_devices_event_dispatcher_unittest.cc",
       "../renderer/media/media_stream_audio_processor_unittest.cc",
       "../renderer/media/media_stream_audio_unittest.cc",
+      "../renderer/media/media_stream_constraints_util_audio_unittest.cc",
       "../renderer/media/media_stream_constraints_util_sets_unittest.cc",
       "../renderer/media/media_stream_constraints_util_unittest.cc",
       "../renderer/media/media_stream_constraints_util_video_content_unittest.cc",
diff --git a/content/test/data/media/image_capture_test.html b/content/test/data/media/image_capture_test.html
index da8da8c8..ff7bce2 100644
--- a/content/test/data/media/image_capture_test.html
+++ b/content/test/data/media/image_capture_test.html
@@ -35,6 +35,25 @@
       });
 }
 
+// Runs an ImageCapture.getPhotoSettings().
+function testCreateAndGetPhotoSettings() {
+  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
+      .then(stream => {
+        assertEquals('video', stream.getVideoTracks()[0].kind);
+        return new ImageCapture(stream.getVideoTracks()[0]);
+      })
+      .then(capturer => {
+        return capturer.getPhotoSettings();
+      })
+      .then(settings => {
+        // There's nothing to check here since |settings| vary per device.
+        reportTestSuccess();
+      })
+      .catch(err => {
+        return failTest(err.toString());
+      });
+}
+
 // Runs an ImageCapture.takePhoto().
 function testCreateAndTakePhoto() {
   navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
diff --git a/docs/speed/apk_size_regressions.md b/docs/speed/apk_size_regressions.md
index 389608d..544b34ea 100644
--- a/docs/speed/apk_size_regressions.md
+++ b/docs/speed/apk_size_regressions.md
@@ -58,7 +58,7 @@
 > Commit: **abc123abc123abc123abc123abc123abc123abcd**
 >
 > Link to size graph:
-> [https://chromeperf.appspot.com/report?sid=a097e74b1aa288511afb4cb616efe0f95ba4d347ad61d5e835072f23450938ba&rev=**440074**](https://chromeperf.appspot.com/report?sid=cfc29eed1238fd38fb5e6cf83bdba6c619be621b606e03e5dfc2e99db14c418b&rev=440074)
+> [https://chromeperf.appspot.com/report?sid=a097e74b1aa288511afb4cb616efe0f95ba4d347ad61d5e835072f23450938ba&num_points=10&rev=**480214**](https://chromeperf.appspot.com/report?sid=a097e74b1aa288511afb4cb616efe0f95ba4d347ad61d5e835072f23450938ba&num_points=10&rev=480214)
 >
 > Debugging size regressions is documented at:
 > https://chromium.googlesource.com/chromium/src/+/master/docs/speed/apk_size_regressions.md#Debugging-Apk-Size-Increase
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 5c0e772..52764808 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -6,8 +6,6 @@
   sources = [
     "content_suggestions_collection_updater.h",
     "content_suggestions_collection_updater.mm",
-    "content_suggestions_collection_utils.h",
-    "content_suggestions_collection_utils.mm",
     "content_suggestions_commands.h",
     "content_suggestions_data_sink.h",
     "content_suggestions_data_source.h",
@@ -16,18 +14,15 @@
     "content_suggestions_view_controller.mm",
   ]
   deps = [
+    ":content_suggestions_util",
     ":resources",
     "//base",
     "//components/strings",
-    "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view",
-    "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/favicon:favicon_ui",
-    "//ios/chrome/browser/ui/toolbar",
-    "//mojo/common:common_custom_types",
     "//ui/base",
   ]
   public_deps = [
@@ -36,6 +31,26 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
+source_set("content_suggestions_util") {
+  sources = [
+    "content_suggestions_collection_utils.h",
+    "content_suggestions_collection_utils.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
+    "//ios/chrome/browser/ui/toolbar",
+    "//ios/third_party/material_components_ios",
+    "//mojo/common:common_custom_types",
+    "//ui/base",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [
@@ -44,6 +59,7 @@
   ]
   deps = [
     ":content_suggestions",
+    ":content_suggestions_util",
     "//base",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
index 9d73f5c..45649a4 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
@@ -9,36 +9,35 @@
 
 namespace content_suggestions {
 
+extern const CGFloat kSearchFieldHeight;
+
 // Returns the maximum number of tiles fitting in |availableWidth|, limited to
 // 4.
 NSUInteger numberOfTilesForWidth(CGFloat availableWidth);
 // Returns the spacing between tiles, based on the device.
 CGFloat spacingBetweenTiles();
 
-// iPhone landscape uses a slightly different layout for the doodle and search
-// field frame. Returns the proper frame from |frames| based on orientation,
-// centered in the view of |width|.
-CGRect getOrientationFrame(const CGRect frames[], CGFloat width);
 // Returns x-offset in order to have the tiles centered in a view with a
 // |width|.
 CGFloat centeredTilesMarginForWidth(CGFloat width);
-// Returns the proper frame for the doodle inside a view with a |width|.
+// Returns the proper height for the doodle. |logoIsShowing| refers to the
+// Google logo or the doodle.
+CGFloat doodleHeight(BOOL logoIsShowing);
+// Returns the proper margin to the top of the header for the doodle.
+CGFloat doodleTopMargin();
+// Returns the proper margin to the top of the header for the search field.
 // |logoIsShowing| refers to the Google logo or the doodle.
-CGRect doodleFrame(CGFloat width, BOOL logoIsShowing);
-// Returns the proper frame for the search field inside a view with a |width|.
-// |logoIsShowing| refers to the Google logo or the doodle.
-CGRect searchFieldFrame(CGFloat width, BOOL logoIsShowing);
+CGFloat searchFieldTopMargin();
+// Returns the proper width for the search field inside a view with a |width|.
+CGFloat searchFieldWidth(CGFloat superviewWidth);
 // Returns the expected height of the NewTabPageHeaderView inside a view with a
 // |width|. |logoIsShowing| refers to the Google logo or the doodle.
 // |promoCanShow| represents whether a what's new promo can be displayed.
-CGFloat heightForLogoHeader(CGFloat width,
-                            BOOL logoIsShowing,
-                            BOOL promoCanShow);
+CGFloat heightForLogoHeader(BOOL logoIsShowing, BOOL promoCanShow);
 // Configure the |searchHintLabel| for the fake omnibox, adding it to the
 // |searchTapTarget| and constrain it.
 void configureSearchHintLabel(UILabel* searchHintLabel,
-                              UIButton* searchTapTarget,
-                              CGFloat searchFieldWidth);
+                              UIButton* searchTapTarget);
 // Configure the |voiceSearchButton|, adding it to the |searchTapTarget| and
 // constraining it.
 void configureVoiceSearchButton(UIButton* voiceSearchButton,
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
index 85ca90b..46174f3 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -23,24 +23,28 @@
 const CGFloat kSpacingIPhone = 16;
 const CGFloat kSpacingIPad = 24;
 
+const CGFloat kMinSearchFieldWidth = 50;
+
+const CGFloat kSearchHintMargin = 3;
+
 const CGFloat kMaxSearchFieldFrameMargin = 200;
-const CGFloat kDoodleTopMarginIPadPortrait = 82;
-const CGFloat kDoodleTopMarginIPadLandscape = 82;
+const CGFloat kDoodleTopMarginIPad = 82;
+const CGFloat kDoodleTopMarginIPhone = 56;
+const CGFloat kSearchFieldTopMarginIPhone = 16;
 const CGFloat kNTPSearchFieldBottomPadding = 16;
 
+const CGFloat kTopSpacingMaterialPortrait = 56;
+const CGFloat kTopSpacingMaterialLandscape = 32;
+
 const CGFloat kVoiceSearchButtonWidth = 48;
 
+const CGFloat kGoogleSearchDoodleHeight = 120;
 // Height for the doodle frame when Google is not the default search engine.
 const CGFloat kNonGoogleSearchDoodleHeight = 60;
 // Height for the header view on tablet when Google is not the default search
 // engine.
 const CGFloat kNonGoogleSearchHeaderHeightIPad = 10;
 
-enum InterfaceOrientation {
-  ALL = 0,
-  IPHONE_LANDSCAPE = 1,
-};
-
 // Returns the width necessary to fit |numberOfItem| items, with no padding on
 // the side.
 CGFloat widthForNumberOfItem(NSUInteger numberOfItem) {
@@ -51,6 +55,8 @@
 
 namespace content_suggestions {
 
+const CGFloat kSearchFieldHeight = 50;
+
 NSUInteger numberOfTilesForWidth(CGFloat availableWidth) {
   if (availableWidth > widthForNumberOfItem(4))
     return 4;
@@ -66,18 +72,6 @@
   return IsIPadIdiom() ? kSpacingIPad : kSpacingIPhone;
 }
 
-CGRect getOrientationFrame(const CGRect frames[], CGFloat width) {
-  BOOL is_portrait = UIInterfaceOrientationIsPortrait(
-      [[UIApplication sharedApplication] statusBarOrientation]);
-  InterfaceOrientation orientation =
-      (IsIPadIdiom() || is_portrait) ? ALL : IPHONE_LANDSCAPE;
-
-  // Calculate width based on screen width and origin x.
-  CGRect frame = frames[orientation];
-  frame.size.width = fmax(width - 2 * frame.origin.x, 50);
-  return frame;
-}
-
 CGFloat centeredTilesMarginForWidth(CGFloat width) {
   NSUInteger columns = numberOfTilesForWidth(width - 2 * spacingBetweenTiles());
   CGFloat whitespace =
@@ -88,41 +82,34 @@
   return margin;
 }
 
-CGRect doodleFrame(CGFloat width, BOOL logoIsShowing) {
-  const CGRect kDoodleFrame[2] = {
-      CGRectMake(0, 66, 0, 120), CGRectMake(0, 56, 0, 120),
-  };
-  CGRect doodleFrame = getOrientationFrame(kDoodleFrame, width);
+CGFloat doodleHeight(BOOL logoIsShowing) {
   if (!IsIPadIdiom() && !logoIsShowing)
-    doodleFrame.size.height = kNonGoogleSearchDoodleHeight;
-  if (IsIPadIdiom()) {
-    doodleFrame.origin.y = IsPortrait() ? kDoodleTopMarginIPadPortrait
-                                        : kDoodleTopMarginIPadLandscape;
-  }
-  return doodleFrame;
+    return kNonGoogleSearchDoodleHeight;
+  return kGoogleSearchDoodleHeight;
 }
 
-CGRect searchFieldFrame(CGFloat width, BOOL logoIsShowing) {
-  CGFloat y = CGRectGetMaxY(doodleFrame(width, logoIsShowing));
-  CGFloat margin = centeredTilesMarginForWidth(width);
+CGFloat doodleTopMargin() {
+  if (IsIPadIdiom())
+    return kDoodleTopMarginIPad;
+  return kDoodleTopMarginIPhone;
+}
+
+CGFloat searchFieldTopMargin() {
+  if (IsIPadIdiom())
+    return kDoodleTopMarginIPad;
+  return kSearchFieldTopMarginIPhone;
+}
+
+CGFloat searchFieldWidth(CGFloat superviewWidth) {
+  CGFloat margin = centeredTilesMarginForWidth(superviewWidth);
   if (margin > kMaxSearchFieldFrameMargin)
     margin = kMaxSearchFieldFrameMargin;
-  const CGRect kSearchFieldFrame[2] = {
-      CGRectMake(margin, y + 32, 0, 50), CGRectMake(margin, y + 16, 0, 50),
-  };
-  CGRect searchFieldFrame = getOrientationFrame(kSearchFieldFrame, width);
-  if (IsIPadIdiom()) {
-    CGFloat iPadTopMargin = IsPortrait() ? kDoodleTopMarginIPadPortrait
-                                         : kDoodleTopMarginIPadLandscape;
-    searchFieldFrame.origin.y += iPadTopMargin - 32;
-  }
-  return searchFieldFrame;
+  return fmax(superviewWidth - 2 * margin, kMinSearchFieldWidth);
 }
 
-CGFloat heightForLogoHeader(CGFloat width,
-                            BOOL logoIsShowing,
-                            BOOL promoCanShow) {
-  CGFloat headerHeight = CGRectGetMaxY(searchFieldFrame(width, logoIsShowing)) +
+CGFloat heightForLogoHeader(BOOL logoIsShowing, BOOL promoCanShow) {
+  CGFloat headerHeight = doodleTopMargin() + doodleHeight(logoIsShowing) +
+                         searchFieldTopMargin() + kSearchFieldHeight +
                          kNTPSearchFieldBottomPadding;
   if (!IsIPadIdiom()) {
     return headerHeight;
@@ -133,8 +120,6 @@
   if (!promoCanShow) {
     UIInterfaceOrientation orient =
         [[UIApplication sharedApplication] statusBarOrientation];
-    const CGFloat kTopSpacingMaterialPortrait = 56;
-    const CGFloat kTopSpacingMaterialLandscape = 32;
     headerHeight += UIInterfaceOrientationIsPortrait(orient)
                         ? kTopSpacingMaterialPortrait
                         : kTopSpacingMaterialLandscape;
@@ -144,18 +129,14 @@
 }
 
 void configureSearchHintLabel(UILabel* searchHintLabel,
-                              UIButton* searchTapTarget,
-                              CGFloat searchFieldWidth) {
-  CGRect hintFrame = CGRectInset(searchTapTarget.bounds, 12, 3);
-  const CGFloat kVoiceSearchOffset = 48;
-  hintFrame.size.width = searchFieldWidth - kVoiceSearchOffset;
+                              UIButton* searchTapTarget) {
   [searchHintLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
 
   [searchTapTarget addSubview:searchHintLabel];
 
   [NSLayoutConstraint activateConstraints:@[
     [searchHintLabel.heightAnchor
-        constraintEqualToConstant:hintFrame.size.height],
+        constraintEqualToConstant:kSearchFieldHeight - 2 * kSearchHintMargin],
     [searchHintLabel.centerYAnchor
         constraintEqualToAnchor:searchTapTarget.centerYAnchor]
   ]];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 1722466..a46a48d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -31,67 +31,26 @@
           return UIUserInterfaceIdiomPhone;
         });
   }
+  void SetAsPortrait() {
+    orientation_swizzler_ = base::MakeUnique<ScopedBlockSwizzler>(
+        [UIApplication class], @selector(statusBarOrientation),
+        ^UIInterfaceOrientation(id self) {
+          return UIInterfaceOrientationPortrait;
+        });
+  }
+  void SetAsLandscape() {
+    orientation_swizzler_ = base::MakeUnique<ScopedBlockSwizzler>(
+        [UIApplication class], @selector(statusBarOrientation),
+        ^UIInterfaceOrientation(id self) {
+          return UIInterfaceOrientationLandscapeLeft;
+        });
+  }
 
  private:
   std::unique_ptr<ScopedBlockSwizzler> device_type_swizzler_;
+  std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler_;
 };
 
-TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFramePortrait) {
-  // Setup.
-  CGRect rect1 = CGRectMake(10, 10, 0, 10);
-  CGRect rect2 = CGRectMake(20, 20, 0, 20);
-  const CGRect rects[2] = {rect1, rect2};
-
-  // Action.
-  CGRect result = getOrientationFrame(rects, 400);
-
-  // Tests.
-  rect1.size.width = 380;
-  EXPECT_TRUE(CGRectEqualToRect(rect1, result));
-}
-
-TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFrameLandscapeIPad) {
-  // Setup.
-  SetAsIPad();
-  CGRect rect1 = CGRectMake(10, 10, 0, 10);
-  CGRect rect2 = CGRectMake(20, 20, 0, 20);
-  const CGRect rects[2] = {rect1, rect2};
-  std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler =
-      base::MakeUnique<ScopedBlockSwizzler>(
-          [UIApplication class], @selector(statusBarOrientation),
-          ^UIInterfaceOrientation(id self) {
-            return UIInterfaceOrientationLandscapeLeft;
-          });
-
-  // Action.
-  CGRect result = getOrientationFrame(rects, 400);
-
-  // Tests.
-  rect1.size.width = 380;
-  EXPECT_TRUE(CGRectEqualToRect(rect1, result));
-}
-
-TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFrameLandscapeIPhone) {
-  // Setup.
-  SetAsIPhone();
-  CGRect rect1 = CGRectMake(10, 10, 0, 10);
-  CGRect rect2 = CGRectMake(20, 20, 0, 20);
-  const CGRect rects[2] = {rect1, rect2};
-  std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler =
-      base::MakeUnique<ScopedBlockSwizzler>(
-          [UIApplication class], @selector(statusBarOrientation),
-          ^UIInterfaceOrientation(id self) {
-            return UIInterfaceOrientationLandscapeLeft;
-          });
-
-  // Action.
-  CGRect result = getOrientationFrame(rects, 400);
-
-  // Tests.
-  rect2.size.width = 360;
-  EXPECT_TRUE(CGRectEqualToRect(rect2, result));
-}
-
 TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPhone6) {
   // Setup.
   SetAsIPhone();
@@ -119,49 +78,94 @@
   SetAsIPad();
 
   // Action.
-  CGRect result = doodleFrame(500, YES);
+  CGFloat height = doodleHeight(YES);
+  CGFloat topMargin = doodleTopMargin();
 
   // Test.
-  EXPECT_TRUE(CGRectEqualToRect(CGRectMake(0, 82, 500, 120), result));
+  EXPECT_EQ(120, height);
+  EXPECT_EQ(82, topMargin);
 }
 
-TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhone) {
+TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhonePortrait) {
   // Setup.
   SetAsIPhone();
+  SetAsPortrait();
 
   // Action.
-  CGRect result = doodleFrame(500, YES);
+  CGFloat heightLogo = doodleHeight(YES);
+  CGFloat heightNoLogo = doodleHeight(NO);
+  CGFloat topMargin = doodleTopMargin();
 
   // Test.
-  EXPECT_TRUE(CGRectEqualToRect(CGRectMake(0, 66, 500, 120), result));
+  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(60, heightNoLogo);
+  EXPECT_EQ(56, topMargin);
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhoneLandscape) {
+  // Setup.
+  SetAsIPhone();
+  SetAsLandscape();
+
+  // Action.
+  CGFloat heightLogo = doodleHeight(YES);
+  CGFloat heightNoLogo = doodleHeight(NO);
+  CGFloat topMargin = doodleTopMargin();
+
+  // Test.
+  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(60, heightNoLogo);
+  EXPECT_EQ(56, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPad) {
   // Setup.
   SetAsIPad();
   CGFloat width = 500;
+  CGFloat largeIPadWidth = 1366;
   CGFloat margin = centeredTilesMarginForWidth(width);
 
   // Action.
-  CGRect result = searchFieldFrame(width, YES);
+  CGFloat resultWidth = searchFieldWidth(width);
+  CGFloat resultWidthLargeIPad = searchFieldWidth(largeIPadWidth);
+  CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  EXPECT_TRUE(
-      CGRectEqualToRect(CGRectMake(margin, 284, 500 - 2 * margin, 50), result));
+  EXPECT_EQ(82, topMargin);
+  EXPECT_EQ(width - 2 * margin, resultWidth);
+  EXPECT_EQ(largeIPadWidth - 400, resultWidthLargeIPad);
 }
 
-TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhone) {
+TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhonePortrait) {
   // Setup.
   SetAsIPhone();
+  SetAsPortrait();
   CGFloat width = 500;
   CGFloat margin = centeredTilesMarginForWidth(width);
 
   // Action.
-  CGRect result = searchFieldFrame(width, YES);
+  CGFloat resultWidth = searchFieldWidth(width);
+  CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  EXPECT_TRUE(
-      CGRectEqualToRect(CGRectMake(margin, 218, 500 - 2 * margin, 50), result));
+  EXPECT_EQ(16, topMargin);
+  EXPECT_EQ(width - 2 * margin, resultWidth);
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhoneLandscape) {
+  // Setup.
+  SetAsIPhone();
+  SetAsLandscape();
+  CGFloat width = 500;
+  CGFloat margin = centeredTilesMarginForWidth(width);
+
+  // Action.
+  CGFloat resultWidth = searchFieldWidth(width);
+  CGFloat topMargin = searchFieldTopMargin();
+
+  // Test.
+  EXPECT_EQ(16, topMargin);
+  EXPECT_EQ(width - 2 * margin, resultWidth);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPad) {
@@ -169,8 +173,8 @@
   SetAsIPad();
 
   // Action, tests.
-  EXPECT_EQ(350, heightForLogoHeader(500, YES, YES));
-  EXPECT_EQ(406, heightForLogoHeader(500, YES, NO));
+  EXPECT_EQ(350, heightForLogoHeader(YES, YES));
+  EXPECT_EQ(406, heightForLogoHeader(YES, NO));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPhone) {
@@ -178,8 +182,8 @@
   SetAsIPhone();
 
   // Action, tests.
-  EXPECT_EQ(284, heightForLogoHeader(500, YES, YES));
-  EXPECT_EQ(284, heightForLogoHeader(500, YES, NO));
+  EXPECT_EQ(258, heightForLogoHeader(YES, YES));
+  EXPECT_EQ(258, heightForLogoHeader(YES, NO));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, SizeIPhone6) {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index e664682..f0312887 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -290,10 +290,12 @@
   UIEdgeInsets inset = [self collectionView:collectionView
                                      layout:collectionView.collectionViewLayout
                      insetForSectionAtIndex:indexPath.section];
+  UIEdgeInsets contentInset = self.collectionView.contentInset;
 
   return [MDCCollectionViewCell
       cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) -
-                                 inset.left - inset.right
+                                 inset.left - inset.right - contentInset.left -
+                                 contentInset.right
                          forItem:item];
 }
 
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index 60285e5..0cf4b664 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -129,6 +129,7 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_util",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/toolbar:resource_macros",
     "//ios/chrome/common",
@@ -204,6 +205,7 @@
     "//ios/chrome/browser/ui/bookmarks",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions",
+    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_util",
     "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/context_menu",
     "//ios/chrome/browser/ui/ntp/recent_tabs",
diff --git a/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm b/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
index 4b467c11..e1455f2 100644
--- a/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
@@ -54,44 +54,6 @@
 
 }  // namespace
 
-@interface GoogleLandingViewController (UsedByGoogleLandingView)
-// Update frames for subviews depending on the interface orientation.
-- (void)updateSubviewFrames;
-// Resets the collection view's inset to 0.
-- (void)resetSectionInset;
-- (void)reloadData;
-@end
-
-// Subclassing the main UIScrollView allows calls for setFrame.
-@interface GoogleLandingView : UIView {
-  GoogleLandingViewController* _googleLanding;
-}
-
-- (void)setFrameDelegate:(GoogleLandingViewController*)delegate;
-
-@end
-
-@implementation GoogleLandingView
-
-- (void)setFrameDelegate:(GoogleLandingViewController*)delegate {
-  _googleLanding = delegate;
-}
-
-- (void)setFrame:(CGRect)frame {
-  // On iPad and in fullscreen, the collection view's inset is very large.
-  // When Chrome enters slide over mode, the previously set inset is larger than
-  // the newly set collection view's width, which makes the collection view
-  // throw an exception.
-  // To prevent this from happening, we reset the inset to 0 before changing the
-  // frame.
-  [_googleLanding resetSectionInset];
-  [super setFrame:frame];
-  [_googleLanding updateSubviewFrames];
-  [_googleLanding reloadData];
-}
-
-@end
-
 @interface GoogleLandingViewController ()<OverscrollActionsControllerDelegate,
                                           UICollectionViewDataSource,
                                           UICollectionViewDelegate,
@@ -141,6 +103,11 @@
   CGSize _mostVisitedCellSize;
   base::scoped_nsobject<NSLayoutConstraint> _hintLabelLeadingConstraint;
   base::scoped_nsobject<NSLayoutConstraint> _voiceTapTrailingConstraint;
+  base::scoped_nsobject<NSLayoutConstraint> _doodleHeightConstraint;
+  base::scoped_nsobject<NSLayoutConstraint> _doodleTopMarginConstraint;
+  base::scoped_nsobject<NSLayoutConstraint> _searchFieldWidthConstraint;
+  base::scoped_nsobject<NSLayoutConstraint> _searchFieldHeightConstraint;
+  base::scoped_nsobject<NSLayoutConstraint> _searchFieldTopMarginConstraint;
   base::scoped_nsobject<NSMutableArray> _supplementaryViews;
   base::scoped_nsobject<NewTabPageHeaderView> _headerView;
   base::scoped_nsobject<WhatsNewHeaderView> _promoHeaderView;
@@ -148,10 +115,6 @@
   base::WeakNSProtocol<id<UrlLoader, OmniboxFocuser>> _dispatcher;
 }
 
-// Redeclare the |view| property to be the GoogleLandingView subclass instead of
-// a generic UIView.
-@property(nonatomic, readwrite, strong) GoogleLandingView* view;
-
 // Whether the Google logo or doodle is being shown.
 @property(nonatomic, assign) BOOL logoIsShowing;
 
@@ -188,6 +151,9 @@
 // pushed into the header view.
 @property(nonatomic, assign) BOOL canGoBack;
 
+// Left margin to center the items. Used for the inset.
+@property(nonatomic, assign) CGFloat leftMargin;
+
 // Returns the height to use for the What's New promo view.
 - (CGFloat)promoHeaderHeight;
 // Add fake search field and voice search microphone.
@@ -212,10 +178,13 @@
 // If Google is not the default search engine, hide the logo, doodle and
 // fakebox.
 - (void)updateLogoAndFakeboxDisplay;
-// Helper method to set UICollectionViewFlowLayout insets for most visited.
-- (void)setFlowLayoutInset:(UICollectionViewFlowLayout*)layout;
 // Instructs the UICollectionView and UIView to reload it's data and layout.
 - (void)reloadData;
+// Adds the constraints for the |logoView|, the |searchField| related to the
+// |headerView|. It also creates the ivar constraints related to those views.
+- (void)addConstraintsForLogoView:(UIView*)logoView
+                      searchField:(UIView*)searchField
+                    andHeaderView:(UIView*)headerView;
 // Returns the size of |self.mostVisitedData|.
 - (NSUInteger)numberOfItems;
 // Returns the number of non empty tiles (as opposed to the placeholder tiles).
@@ -234,7 +203,6 @@
 
 @implementation GoogleLandingViewController
 
-@dynamic view;
 @synthesize logoVendor = _logoVendor;
 // Property declared in NewTabPagePanelProtocol.
 @synthesize delegate = _delegate;
@@ -248,17 +216,12 @@
 @synthesize canGoForward = _canGoForward;
 @synthesize canGoBack = _canGoBack;
 @synthesize voiceSearchIsEnabled = _voiceSearchIsEnabled;
-
-- (void)loadView {
-  self.view = [[[GoogleLandingView alloc]
-      initWithFrame:[UIScreen mainScreen].bounds] autorelease];
-}
+@synthesize leftMargin = _leftMargin;
 
 - (void)viewDidLoad {
   [super viewDidLoad];
   [self.view setAutoresizingMask:UIViewAutoresizingFlexibleHeight |
                                  UIViewAutoresizingFlexibleWidth];
-  [self.view setFrameDelegate:self];
 
   // Initialise |shiftTilesDownStartTime| to a sentinel value to indicate that
   // the animation has not yet started.
@@ -277,14 +240,15 @@
               action:@selector(blurOmnibox)]);
   [_swipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown];
 
+  self.leftMargin =
+      content_suggestions::centeredTilesMarginForWidth([self viewWidth]);
+
   [self addSearchField];
   [self addMostVisited];
   [self addOverscrollActions];
   [self reload];
-}
-
-- (void)viewDidLayoutSubviews {
-  [self updateSubviewFrames];
+  _viewLoaded = YES;
+  [self.logoVendor fetchDoodle];
 }
 
 - (void)viewWillTransitionToSize:(CGSize)size
@@ -292,6 +256,13 @@
            (id<UIViewControllerTransitionCoordinator>)coordinator {
   [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
 
+  self.leftMargin =
+      content_suggestions::centeredTilesMarginForWidth(size.width);
+
+  // Invalidate the layout so that the collection view's header size is reset
+  // for the new orientation.
+  [[_mostVisitedView collectionViewLayout] invalidateLayout];
+
   void (^alongsideBlock)(id<UIViewControllerTransitionCoordinatorContext>) = ^(
       id<UIViewControllerTransitionCoordinatorContext> context) {
     if (IsIPadIdiom() && _scrolledToTop) {
@@ -300,16 +271,33 @@
       return;
     };
 
-    // Invalidate the layout so that the collection view's header size is reset
-    // for the new orientation.
-    if (!_scrolledToTop) {
-      [[_mostVisitedView collectionViewLayout] invalidateLayout];
-    }
-
     // Call -scrollViewDidScroll: so that the omnibox's frame is adjusted for
     // the scroll view's offset.
     [self scrollViewDidScroll:_mostVisitedView];
 
+    // Updates the constraints.
+    [_promoHeaderView
+        setSideMargin:content_suggestions::centeredTilesMarginForWidth(
+                          size.width)
+             forWidth:size.width];
+    [_doodleTopMarginConstraint
+        setConstant:content_suggestions::doodleTopMargin()];
+    [_searchFieldWidthConstraint
+        setConstant:content_suggestions::searchFieldWidth(size.width)];
+    [_searchFieldTopMarginConstraint
+        setConstant:content_suggestions::searchFieldTopMargin()];
+    BOOL isScrollableNTP = !IsIPadIdiom() || IsCompactTablet();
+    if (isScrollableNTP && _scrolledToTop) {
+      // Set the scroll view's offset to the pinned offset to keep the omnibox
+      // at the top of the screen if it isn't already.
+      CGFloat pinnedOffsetY = [self pinnedOffsetY];
+      if ([_mostVisitedView contentOffset].y < pinnedOffsetY) {
+        [_mostVisitedView setContentOffset:CGPointMake(0, pinnedOffsetY)];
+      } else {
+        [self updateSearchField];
+      }
+    }
+
   };
   [coordinator animateAlongsideTransition:alongsideBlock completion:nil];
 }
@@ -387,13 +375,14 @@
   if (self.logoVendor.showingLogo != self.logoIsShowing) {
     self.logoVendor.showingLogo = self.logoIsShowing;
     if (_viewLoaded) {
-      [self updateSubviewFrames];
+      [_doodleHeightConstraint
+          setConstant:content_suggestions::doodleHeight(self.logoIsShowing)];
 
       // Adjust the height of |_headerView| to fit its content which may have
       // been shifted due to the visibility of the doodle.
       CGRect headerFrame = [_headerView frame];
       headerFrame.size.height = content_suggestions::heightForLogoHeader(
-          [self viewWidth], self.logoIsShowing, self.promoCanShow);
+          self.logoIsShowing, self.promoCanShow);
       [_headerView setFrame:headerFrame];
 
       // Adjust vertical positioning of |_promoHeaderView|.
@@ -414,9 +403,7 @@
 
 // Initialize and add a search field tap target and a voice search button.
 - (void)addSearchField {
-  CGRect searchFieldFrame = content_suggestions::searchFieldFrame(
-      [self viewWidth], self.logoIsShowing);
-  _searchTapTarget.reset([[UIButton alloc] initWithFrame:searchFieldFrame]);
+  _searchTapTarget.reset([[UIButton alloc] init]);
   if (IsIPadIdiom()) {
     UIImage* searchBoxImage = [[UIImage imageNamed:@"ntp_google_search_box"]
         resizableImageWithCapInsets:kSearchBoxStretchInsets];
@@ -434,8 +421,8 @@
 
   // Set up fakebox hint label.
   UILabel* searchHintLabel = [[[UILabel alloc] init] autorelease];
-  content_suggestions::configureSearchHintLabel(
-      searchHintLabel, _searchTapTarget.get(), searchFieldFrame.size.width);
+  content_suggestions::configureSearchHintLabel(searchHintLabel,
+                                                _searchTapTarget.get());
 
   _hintLabelLeadingConstraint.reset([[searchHintLabel.leadingAnchor
       constraintEqualToAnchor:[_searchTapTarget leadingAnchor]
@@ -486,69 +473,6 @@
   [sender chromeExecuteCommand:command];
 }
 
-- (void)setFlowLayoutInset:(UICollectionViewFlowLayout*)layout {
-  CGFloat leftMargin =
-      content_suggestions::centeredTilesMarginForWidth([self viewWidth]);
-  [layout setSectionInset:UIEdgeInsetsMake(0, leftMargin, 0, leftMargin)];
-}
-
-- (void)resetSectionInset {
-  UICollectionViewFlowLayout* flowLayout =
-      (UICollectionViewFlowLayout*)[_mostVisitedView collectionViewLayout];
-  [flowLayout setSectionInset:UIEdgeInsetsZero];
-}
-
-- (void)updateSubviewFrames {
-  _mostVisitedCellSize = [self mostVisitedCellSize];
-  UICollectionViewFlowLayout* flowLayout =
-      base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
-          [_mostVisitedView collectionViewLayout]);
-  [flowLayout setItemSize:_mostVisitedCellSize];
-  self.logoVendor.view.frame =
-      content_suggestions::doodleFrame([self viewWidth], self.logoIsShowing);
-
-  [self setFlowLayoutInset:flowLayout];
-  [flowLayout invalidateLayout];
-  [_promoHeaderView
-      setSideMargin:content_suggestions::centeredTilesMarginForWidth(
-                        [self viewWidth])];
-
-  // On the iPhone 6 Plus, if the app is started in landscape after a fresh
-  // install, the UICollectionViewLayout incorrectly sizes the widths of the
-  // supplementary views to the portrait width.  Correct that here to ensure
-  // that the header is property laid out to the UICollectionView's width.
-  // crbug.com/491131
-  CGFloat collectionViewWidth = CGRectGetWidth([_mostVisitedView bounds]);
-  CGFloat collectionViewMinX = CGRectGetMinX([_mostVisitedView bounds]);
-  for (UIView* supplementaryView in _supplementaryViews.get()) {
-    CGRect supplementaryViewFrame = supplementaryView.frame;
-    supplementaryViewFrame.origin.x = collectionViewMinX;
-    supplementaryViewFrame.size.width = collectionViewWidth;
-    supplementaryView.frame = supplementaryViewFrame;
-  }
-
-  BOOL isScrollableNTP = !IsIPadIdiom() || IsCompactTablet();
-  if (isScrollableNTP && _scrolledToTop) {
-    // Set the scroll view's offset to the pinned offset to keep the omnibox
-    // at the top of the screen if it isn't already.
-    CGFloat pinnedOffsetY = [self pinnedOffsetY];
-    if ([_mostVisitedView contentOffset].y < pinnedOffsetY) {
-      [_mostVisitedView setContentOffset:CGPointMake(0, pinnedOffsetY)];
-    } else {
-      [self updateSearchField];
-    }
-  } else {
-    [_searchTapTarget setFrame:content_suggestions::searchFieldFrame(
-                                   [self viewWidth], self.logoIsShowing)];
-  }
-
-  if (!_viewLoaded) {
-    _viewLoaded = YES;
-    [self.logoVendor fetchDoodle];
-  }
-  [self.delegate updateNtpBarShadowForPanelController:self];
-}
-
 // Initialize and add a panel with most visited sites.
 - (void)addMostVisited {
   CGRect mostVisitedFrame = [self.view bounds];
@@ -590,11 +514,13 @@
 - (void)updateSearchField {
   NSArray* constraints =
       @[ _hintLabelLeadingConstraint, _voiceTapTrailingConstraint ];
-  [_headerView updateSearchField:_searchTapTarget
-                withInitialFrame:content_suggestions::searchFieldFrame(
-                                     [self viewWidth], self.logoIsShowing)
-              subviewConstraints:constraints
-                       forOffset:[_mostVisitedView contentOffset].y];
+
+  [_headerView updateSearchFieldWidth:_searchFieldWidthConstraint
+                               height:_searchFieldHeightConstraint
+                            topMargin:_searchFieldTopMarginConstraint
+                   subviewConstraints:constraints
+                        logoIsShowing:self.logoIsShowing
+                            forOffset:[_mostVisitedView contentOffset].y];
 }
 
 - (void)addOverscrollActions {
@@ -750,6 +676,38 @@
   [self.view setNeedsLayout];
 }
 
+- (void)addConstraintsForLogoView:(UIView*)logoView
+                      searchField:(UIView*)searchField
+                    andHeaderView:(UIView*)headerView {
+  _doodleTopMarginConstraint.reset([[logoView.topAnchor
+      constraintEqualToAnchor:headerView.topAnchor
+                     constant:content_suggestions::doodleTopMargin()] retain]);
+  _doodleHeightConstraint.reset([[logoView.heightAnchor
+      constraintEqualToConstant:content_suggestions::doodleHeight(
+                                    self.logoIsShowing)] retain]);
+  _searchFieldWidthConstraint.reset([[searchField.widthAnchor
+      constraintEqualToConstant:content_suggestions::searchFieldWidth(
+                                    [self viewWidth])] retain]);
+  _searchFieldHeightConstraint.reset([[searchField.heightAnchor
+      constraintEqualToConstant:content_suggestions::kSearchFieldHeight]
+      retain]);
+  _searchFieldTopMarginConstraint.reset([[searchField.topAnchor
+      constraintEqualToAnchor:logoView.bottomAnchor
+                     constant:content_suggestions::searchFieldTopMargin()]
+      retain]);
+  [NSLayoutConstraint activateConstraints:@[
+    _doodleTopMarginConstraint,
+    _doodleHeightConstraint.get(),
+    _searchFieldWidthConstraint.get(),
+    _searchFieldHeightConstraint.get(),
+    _searchFieldTopMarginConstraint.get(),
+    [logoView.widthAnchor constraintEqualToAnchor:headerView.widthAnchor],
+    [logoView.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor],
+    [searchField.centerXAnchor
+        constraintEqualToAnchor:headerView.centerXAnchor],
+  ]];
+}
+
 #pragma mark - ToolbarOwner
 
 - (ToolbarController*)relinquishedToolbarController {
@@ -762,14 +720,20 @@
 
 #pragma mark - UICollectionView Methods.
 
+- (UIEdgeInsets)collectionView:(UICollectionView*)collectionView
+                        layout:(UICollectionViewLayout*)collectionViewLayout
+        insetForSectionAtIndex:(NSInteger)section {
+  return UIEdgeInsetsMake(0, self.leftMargin, 0, self.leftMargin);
+}
+
 - (CGSize)collectionView:(UICollectionView*)collectionView
                              layout:
                                  (UICollectionViewLayout*)collectionViewLayout
     referenceSizeForHeaderInSection:(NSInteger)section {
   CGFloat headerHeight = 0;
   if (section == SectionWithOmnibox) {
-    headerHeight = content_suggestions::heightForLogoHeader(
-        [self viewWidth], self.logoIsShowing, self.promoCanShow);
+    headerHeight = content_suggestions::heightForLogoHeader(self.logoIsShowing,
+                                                            self.promoCanShow);
     ((UICollectionViewFlowLayout*)collectionViewLayout).headerReferenceSize =
         CGSizeMake(0, headerHeight);
   } else if (section == SectionWithMostVisited) {
@@ -836,6 +800,13 @@
                                     forIndexPath:indexPath] retain]);
       [_headerView addSubview:[self.logoVendor view]];
       [_headerView addSubview:_searchTapTarget];
+      self.logoVendor.view.translatesAutoresizingMaskIntoConstraints = NO;
+      [_searchTapTarget setTranslatesAutoresizingMaskIntoConstraints:NO];
+
+      [self addConstraintsForLogoView:self.logoVendor.view
+                          searchField:_searchTapTarget
+                        andHeaderView:_headerView];
+
       [_headerView addViewsToSearchField:_searchTapTarget];
 
       if (!IsIPadIdiom()) {
@@ -861,7 +832,8 @@
                                     forIndexPath:indexPath] retain]);
       [_promoHeaderView
           setSideMargin:content_suggestions::centeredTilesMarginForWidth(
-                            [self viewWidth])];
+                            [self viewWidth])
+               forWidth:[self viewWidth]];
       [_promoHeaderView setDelegate:self];
       if (self.promoCanShow) {
         [_promoHeaderView setText:self.promoText];
@@ -1067,7 +1039,8 @@
 // Returns the Y value to use for the scroll view's contentOffset when scrolling
 // the omnibox to the top of the screen.
 - (CGFloat)pinnedOffsetY {
-  CGFloat headerHeight = [_headerView frame].size.height;
+  CGFloat headerHeight = content_suggestions::heightForLogoHeader(
+      self.logoIsShowing, self.promoCanShow);
   CGFloat offsetY =
       headerHeight - ntp_header::kScrolledToTopOmniboxBottomMargin;
   if (!IsIPadIdiom())
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
index 64ea947..83911a7e 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
@@ -24,13 +24,15 @@
 - (void)addToolbarWithDataSource:(id<GoogleLandingDataSource>)dataSource
                       dispatcher:(id)dispatcher;
 
-// Changes the frame of |searchField| based on its |initialFrame| and the scroll
-// view's y |offset|. Also adjust the alpha values for |_searchBoxBorder| and
-// |_shadow| and the constant values for the |constraints|.
-- (void)updateSearchField:(UIView*)searchField
-         withInitialFrame:(CGRect)initialFrame
-       subviewConstraints:(NSArray*)constraints
-                forOffset:(CGFloat)offset;
+// Changes the constraints of searchField based on its initialFrame and the
+// scroll view's y |offset|. Also adjust the alpha values for |_searchBoxBorder|
+// and |_shadow| and the constant values for the |constraints|.
+- (void)updateSearchFieldWidth:(NSLayoutConstraint*)widthConstraint
+                        height:(NSLayoutConstraint*)heightConstraint
+                     topMargin:(NSLayoutConstraint*)topMarginConstraint
+            subviewConstraints:(NSArray*)constraints
+                 logoIsShowing:(BOOL)logoIsShowing
+                     forOffset:(CGFloat)offset;
 
 // Initializes |_searchBoxBorder| and |_shadow| and adds them to |searchField|.
 - (void)addViewsToSearchField:(UIView*)searchField;
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 f7dc530..945cc38 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
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/image_util.h"
 #import "ios/chrome/browser/ui/ntp/google_landing_data_source.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
@@ -79,7 +80,6 @@
   toolbarView.frame = toolbarFrame;
   [toolbarView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
 
-  [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
   [self addSubview:[_toolbarController view]];
 }
 
@@ -125,14 +125,16 @@
   [_shadow setAlpha:0];
 }
 
-- (void)updateSearchField:(UIView*)searchField
-         withInitialFrame:(CGRect)initialFrame
-       subviewConstraints:(NSArray*)constraints
-                forOffset:(CGFloat)offset {
-  // The scroll offset at which point |searchField|'s frame should stop growing.
+- (void)updateSearchFieldWidth:(NSLayoutConstraint*)widthConstraint
+                        height:(NSLayoutConstraint*)heightConstraint
+                     topMargin:(NSLayoutConstraint*)topMarginConstraint
+            subviewConstraints:(NSArray*)constraints
+                 logoIsShowing:(BOOL)logoIsShowing
+                     forOffset:(CGFloat)offset {
+  // The scroll offset at which point searchField's frame should stop growing.
   CGFloat maxScaleOffset =
       self.frame.size.height - ntp_header::kMinHeaderHeight;
-  // The scroll offset at which point |searchField|'s frame should start
+  // The scroll offset at which point searchField's frame should start
   // growing.
   CGFloat startScaleOffset = maxScaleOffset - ntp_header::kAnimationDistance;
   CGFloat percent = 0;
@@ -141,17 +143,24 @@
     percent = MIN(1, MAX(0, animatingOffset / ntp_header::kAnimationDistance));
   }
 
-  // Calculate the amount to grow the width and height of |searchField| so that
+  CGFloat searchFieldNormalWidth =
+      content_suggestions::searchFieldWidth(self.bounds.size.width);
+
+  // Calculate the amount to grow the width and height of searchField so that
   // its frame covers the entire toolbar area.
   CGFloat maxXInset = ui::AlignValueToUpperPixel(
-      (initialFrame.size.width - self.bounds.size.width) / 2 - 1);
+      (searchFieldNormalWidth - self.bounds.size.width) / 2 - 1);
   CGFloat maxYOffset = ui::AlignValueToUpperPixel(
-      (ntp_header::kToolbarHeight - initialFrame.size.height) / 2 +
+      (ntp_header::kToolbarHeight - content_suggestions::kSearchFieldHeight) /
+          2 +
       kOmniboxImageBottomInset - 0.5);
-  CGRect searchFieldFrame = CGRectInset(initialFrame, maxXInset * percent, 0);
-  searchFieldFrame.origin.y += maxYOffset * percent;
-  searchFieldFrame.size.height += 2 * maxYOffset * percent;
-  [searchField setFrame:CGRectIntegral(searchFieldFrame)];
+
+  widthConstraint.constant = searchFieldNormalWidth - 2 * maxXInset * percent;
+  topMarginConstraint.constant =
+      content_suggestions::searchFieldTopMargin() + maxYOffset * percent;
+  heightConstraint.constant =
+      content_suggestions::kSearchFieldHeight + 2 * maxYOffset * percent;
+
   [_searchBoxBorder setAlpha:(1 - percent)];
   [_shadow setAlpha:percent];
 
diff --git a/ios/chrome/browser/ui/ntp/whats_new_header_view.h b/ios/chrome/browser/ui/ntp/whats_new_header_view.h
index ac306164..b87a138 100644
--- a/ios/chrome/browser/ui/ntp/whats_new_header_view.h
+++ b/ios/chrome/browser/ui/ntp/whats_new_header_view.h
@@ -27,7 +27,7 @@
 - (void)setIcon:(WhatsNewIcon)icon;
 
 // Sets the value to use for the left and right margin.
-- (void)setSideMargin:(CGFloat)sideMargin;
+- (void)setSideMargin:(CGFloat)sideMargin forWidth:(CGFloat)width;
 
 // Returns the minimum height required for WhatsNewHeaderView to fit in |width|,
 // for a given |text|.
diff --git a/ios/chrome/browser/ui/ntp/whats_new_header_view.mm b/ios/chrome/browser/ui/ntp/whats_new_header_view.mm
index c4a7aef6..43c43f9 100644
--- a/ios/chrome/browser/ui/ntp/whats_new_header_view.mm
+++ b/ios/chrome/browser/ui/ntp/whats_new_header_view.mm
@@ -131,11 +131,11 @@
   [_infoIconImageView setImage:image];
 }
 
-- (void)setSideMargin:(CGFloat)sideMargin {
+- (void)setSideMargin:(CGFloat)sideMargin forWidth:(CGFloat)width {
   _sideMargin = sideMargin;
   [self setNeedsUpdateConstraints];
   CGFloat maxLabelWidth =
-      self.frame.size.width - 2 * sideMargin - kInfoIconSize - kLabelLeftMargin;
+      width - 2 * sideMargin - kInfoIconSize - kLabelLeftMargin;
   [_promoLabel.get() setPreferredMaxLayoutWidth:maxLabelWidth];
 }
 
diff --git a/ios/net/crn_http_protocol_handler.mm b/ios/net/crn_http_protocol_handler.mm
index bdfe4527..372042b5 100644
--- a/ios/net/crn_http_protocol_handler.mm
+++ b/ios/net/crn_http_protocol_handler.mm
@@ -534,10 +534,7 @@
     while (bytes_read > 0 &&
            [data length] + bytes_read <= kClientMaxBufferSize) {
       total_bytes_read += bytes_read;
-      NSUInteger data_length = [data length];
-      [data increaseLengthBy:bytes_read];
-      memcpy(reinterpret_cast<char*>([data mutableBytes]) + data_length,
-             buffer_->data(), bytes_read);
+      [data appendBytes:buffer_->data() length:bytes_read];
       bytes_read = request->Read(buffer_.get(), kIOBufferSize);
     }
 
diff --git a/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h b/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
index 976379b..f97a6b72 100644
--- a/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
+++ b/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
@@ -42,7 +42,7 @@
   virtual bool IsUserFeedbackEnabled();
   // Returns view controller to present to the user to collect their feedback.
   virtual UIViewController* CreateViewController(
-      id<UserFeedbackDataSource> dataSource) NS_RETURNS_RETAINED;
+      id<UserFeedbackDataSource> dataSource);
   // Uploads collected feedback reports.
   virtual void Synchronize();
 
diff --git a/media/blink/webmediaplayer_delegate.h b/media/blink/webmediaplayer_delegate.h
index e4949b17..1dd0b7a 100644
--- a/media/blink/webmediaplayer_delegate.h
+++ b/media/blink/webmediaplayer_delegate.h
@@ -94,6 +94,9 @@
   // Notify that the size of the media player is changed.
   virtual void DidPlayerSizeChange(int delegate_id, const gfx::Size& size) = 0;
 
+  // Notify that the muted status of the media player has changed.
+  virtual void DidPlayerMutedStatusChange(int delegate_id, bool muted) = 0;
+
   // Notify that playback is stopped. This will drop wake locks and remove any
   // external controls.
   //
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 0b3c60a2..f37e0f1 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -673,6 +673,7 @@
   pipeline_controller_.SetVolume(volume_ * volume_multiplier_);
   if (watch_time_reporter_)
     watch_time_reporter_->OnVolumeChange(volume);
+  delegate_->DidPlayerMutedStatusChange(delegate_id_, volume == 0.0);
 
   // The play state is updated because the player might have left the autoplay
   // muted state.
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 1a5347cf..4915476 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -144,6 +144,10 @@
     return is_idle_;
   }
 
+  void DidPlayerMutedStatusChange(int delegate_id, bool muted) override {
+    DCHECK_EQ(player_id_, delegate_id);
+  }
+
   void ClearStaleFlag(int player_id) override {
     DCHECK_EQ(player_id_, player_id);
     is_stale_ = false;
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc
index aed9658..d5b0aee 100644
--- a/media/gpu/h264_decoder.cc
+++ b/media/gpu/h264_decoder.cc
@@ -177,6 +177,8 @@
            sizeof(curr_pic_->ref_pic_marking));
   }
 
+  curr_pic_->visible_rect = visible_rect_;
+
   return true;
 }
 
@@ -1124,6 +1126,12 @@
     dpb_.set_max_num_pics(max_dpb_size);
   }
 
+  gfx::Rect new_visible_rect = sps->GetVisibleRect().value_or(gfx::Rect());
+  if (visible_rect_ != new_visible_rect) {
+    DVLOG(2) << "New visible rect: " << new_visible_rect.ToString();
+    visible_rect_ = new_visible_rect;
+  }
+
   if (!UpdateMaxNumReorderFrames(sps))
     return false;
   DVLOG(1) << "max_num_reorder_frames: " << max_num_reorder_frames_;
diff --git a/media/gpu/h264_decoder.h b/media/gpu/h264_decoder.h
index 1caf662..a0daf69 100644
--- a/media/gpu/h264_decoder.h
+++ b/media/gpu/h264_decoder.h
@@ -18,6 +18,7 @@
 #include "media/gpu/accelerated_video_decoder.h"
 #include "media/gpu/h264_dpb.h"
 #include "media/gpu/media_gpu_export.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
@@ -266,6 +267,8 @@
 
   // Output picture size.
   gfx::Size pic_size_;
+  // Output visible cropping rect.
+  gfx::Rect visible_rect_;
 
   // PicOrderCount of the previously outputted frame.
   int last_output_poc_;
diff --git a/media/gpu/h264_dpb.h b/media/gpu/h264_dpb.h
index aef39a1b..e8f119f 100644
--- a/media/gpu/h264_dpb.h
+++ b/media/gpu/h264_dpb.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "media/filters/h264_parser.h"
+#include "ui/gfx/geometry/rect.h"
 
 namespace media {
 
@@ -84,6 +85,10 @@
   // Position in DPB (i.e. index in DPB).
   int dpb_position;
 
+  // The visible size of picture. This could be either parsed from SPS, or set
+  // to gfx::Rect(0, 0) for indicating invalid values or not available.
+  gfx::Rect visible_rect;
+
  protected:
   friend class base::RefCounted<H264Picture>;
   virtual ~H264Picture();
diff --git a/media/gpu/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2_slice_video_decode_accelerator.cc
index 2622312..f4760060 100644
--- a/media/gpu/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2_slice_video_decode_accelerator.cc
@@ -89,6 +89,11 @@
   int input_record() const { return input_record_; }
   int output_record() const { return output_record_; }
   uint32_t config_store() const { return config_store_; }
+  gfx::Rect visible_rect() const { return visible_rect_; }
+
+  void set_visible_rect(const gfx::Rect& visible_rect) {
+    visible_rect_ = visible_rect;
+  }
 
   // Take references to each reference surface and keep them until the
   // target surface is decoded.
@@ -113,6 +118,7 @@
   int input_record_;
   int output_record_;
   uint32_t config_store_;
+  gfx::Rect visible_rect_;
 
   bool decoded_;
   ReleaseCB release_cb_;
@@ -800,18 +806,18 @@
   DCHECK(surfaces_at_display_.empty());
   DCHECK(surfaces_at_device_.empty());
 
-  visible_size_ = decoder_->GetPicSize();
+  gfx::Size pic_size = decoder_->GetPicSize();
   size_t num_pictures = decoder_->GetRequiredNumOfPictures();
 
   DCHECK_GT(num_pictures, 0u);
-  DCHECK(!visible_size_.IsEmpty());
+  DCHECK(!pic_size.IsEmpty());
 
   struct v4l2_format format;
   memset(&format, 0, sizeof(format));
   format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
   format.fmt.pix_mp.pixelformat = output_format_fourcc_;
-  format.fmt.pix_mp.width = visible_size_.width();
-  format.fmt.pix_mp.height = visible_size_.height();
+  format.fmt.pix_mp.width = pic_size.width();
+  format.fmt.pix_mp.height = pic_size.height();
   format.fmt.pix_mp.num_planes = input_planes_count_;
 
   if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
@@ -825,14 +831,14 @@
   DCHECK_EQ(coded_size_.width() % 16, 0);
   DCHECK_EQ(coded_size_.height() % 16, 0);
 
-  if (!gfx::Rect(coded_size_).Contains(gfx::Rect(visible_size_))) {
+  if (!gfx::Rect(coded_size_).Contains(gfx::Rect(pic_size))) {
     LOGF(ERROR) << "Got invalid adjusted coded size: "
                 << coded_size_.ToString();
     return false;
   }
 
   DVLOGF(3) << "buffer_count=" << num_pictures
-            << ", visible size=" << visible_size_.ToString()
+            << ", pic size=" << pic_size.ToString()
             << ", coded size=" << coded_size_.ToString();
 
   // With ALLOCATE mode the client can sample it as RGB and doesn't need to
@@ -2529,6 +2535,7 @@
     const scoped_refptr<H264Picture>& pic) {
   scoped_refptr<V4L2DecodeSurface> dec_surface =
       H264PictureToV4L2DecodeSurface(pic);
+  dec_surface->set_visible_rect(pic->visible_rect);
   v4l2_dec_->SurfaceReady(dec_surface);
   return true;
 }
@@ -2760,7 +2767,7 @@
     const scoped_refptr<VP8Picture>& pic) {
   scoped_refptr<V4L2DecodeSurface> dec_surface =
       VP8PictureToV4L2DecodeSurface(pic);
-
+  dec_surface->set_visible_rect(pic->visible_rect);
   v4l2_dec_->SurfaceReady(dec_surface);
   return true;
 }
@@ -3063,7 +3070,7 @@
     const scoped_refptr<VP9Picture>& pic) {
   scoped_refptr<V4L2DecodeSurface> dec_surface =
       VP9PictureToV4L2DecodeSurface(pic);
-
+  dec_surface->set_visible_rect(pic->visible_rect);
   v4l2_dec_->SurfaceReady(dec_surface);
   return true;
 }
@@ -3187,15 +3194,13 @@
   DCHECK_NE(output_record.picture_id, -1);
   output_record.at_client = true;
 
-  // TODO(posciak): Use visible size from decoder here instead
-  // (crbug.com/402760). Passing (0, 0) results in the client using the
-  // visible size extracted from the container instead.
   // TODO(hubbe): Insert correct color space. http://crbug.com/647725
   Picture picture(output_record.picture_id, dec_surface->bitstream_id(),
-                  gfx::Rect(0, 0), gfx::ColorSpace(), false);
+                  dec_surface->visible_rect(), gfx::ColorSpace(), false);
   DVLOGF(3) << dec_surface->ToString()
             << ", bitstream_id: " << picture.bitstream_buffer_id()
-            << ", picture_id: " << picture.picture_buffer_id();
+            << ", picture_id: " << picture.picture_buffer_id()
+            << ", visible_rect: " << picture.visible_rect().ToString();
   pending_picture_ready_.push(PictureRecord(output_record.cleared, picture));
   SendPictureReady();
   output_record.cleared = true;
diff --git a/media/gpu/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2_slice_video_decode_accelerator.h
index b8525f67..311ffe9a 100644
--- a/media/gpu/v4l2_slice_video_decode_accelerator.h
+++ b/media/gpu/v4l2_slice_video_decode_accelerator.h
@@ -405,7 +405,6 @@
   VideoCodecProfile video_profile_;
   uint32_t input_format_fourcc_;
   uint32_t output_format_fourcc_;
-  gfx::Size visible_size_;
   gfx::Size coded_size_;
 
   struct BitstreamBufferRef;
diff --git a/media/gpu/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi_video_decode_accelerator.cc
index 9c6897f..4c80a0d 100644
--- a/media/gpu/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi_video_decode_accelerator.cc
@@ -67,6 +67,11 @@
 
   int32_t bitstream_id() const { return bitstream_id_; }
   scoped_refptr<VASurface> va_surface() { return va_surface_; }
+  gfx::Rect visible_rect() const { return visible_rect_; }
+
+  void set_visible_rect(const gfx::Rect& visible_rect) {
+    visible_rect_ = visible_rect;
+  }
 
  private:
   friend class base::RefCountedThreadSafe<VaapiDecodeSurface>;
@@ -74,6 +79,7 @@
 
   int32_t bitstream_id_;
   scoped_refptr<VASurface> va_surface_;
+  gfx::Rect visible_rect_;
 };
 
 VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface(
@@ -409,6 +415,7 @@
 void VaapiVideoDecodeAccelerator::OutputPicture(
     const scoped_refptr<VASurface>& va_surface,
     int32_t input_id,
+    gfx::Rect visible_rect,
     VaapiPicture* picture) {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
@@ -427,14 +434,12 @@
   // Notify the client a picture is ready to be displayed.
   ++num_frames_at_client_;
   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
-  DVLOG(4) << "Notifying output picture id " << output_id
-           << " for input " << input_id << " is ready";
-  // TODO(posciak): Use visible size from decoder here instead
-  // (crbug.com/402760). Passing (0, 0) results in the client using the
-  // visible size extracted from the container instead.
+  DVLOG(4) << "Notifying output picture id " << output_id << " for input "
+           << input_id
+           << " is ready. visible rect: " << visible_rect.ToString();
   // TODO(hubbe): Use the correct color space.  http://crbug.com/647725
   if (client_)
-    client_->PictureReady(Picture(output_id, input_id, gfx::Rect(0, 0),
+    client_->PictureReady(Picture(output_id, input_id, visible_rect,
                                   gfx::ColorSpace(), picture->AllowOverlay()));
 }
 
@@ -1143,7 +1148,8 @@
 
   pending_output_cbs_.push(
       base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, weak_this_,
-                 dec_surface->va_surface(), dec_surface->bitstream_id()));
+                 dec_surface->va_surface(), dec_surface->bitstream_id(),
+                 dec_surface->visible_rect()));
 
   TryOutputSurface();
 }
@@ -1426,7 +1432,7 @@
     const scoped_refptr<H264Picture>& pic) {
   scoped_refptr<VaapiDecodeSurface> dec_surface =
       H264PictureToVaapiDecodeSurface(pic);
-
+  dec_surface->set_visible_rect(pic->visible_rect);
   vaapi_dec_->SurfaceReady(dec_surface);
 
   return true;
@@ -1713,7 +1719,7 @@
     const scoped_refptr<VP8Picture>& pic) {
   scoped_refptr<VaapiDecodeSurface> dec_surface =
       VP8PictureToVaapiDecodeSurface(pic);
-
+  dec_surface->set_visible_rect(pic->visible_rect);
   vaapi_dec_->SurfaceReady(dec_surface);
   return true;
 }
@@ -1877,7 +1883,7 @@
     const scoped_refptr<VP9Picture>& pic) {
   scoped_refptr<VaapiDecodeSurface> dec_surface =
       VP9PictureToVaapiDecodeSurface(pic);
-
+  dec_surface->set_visible_rect(pic->visible_rect);
   vaapi_dec_->SurfaceReady(dec_surface);
   return true;
 }
diff --git a/media/gpu/vaapi_video_decode_accelerator.h b/media/gpu/vaapi_video_decode_accelerator.h
index 241c002..9edf1707 100644
--- a/media/gpu/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi_video_decode_accelerator.h
@@ -145,10 +145,12 @@
 
   // Callback to be executed once we have a |va_surface| to be output and
   // an available |picture| to use for output.
-  // Puts contents of |va_surface| into given |picture|, releases the
-  // surface and passes the resulting picture to client for output.
+  // Puts contents of |va_surface| into given |picture|, releases the surface
+  // and passes the resulting picture to client to output the given
+  // |visible_rect| part of it.
   void OutputPicture(const scoped_refptr<VASurface>& va_surface,
                      int32_t input_id,
+                     gfx::Rect visible_rect,
                      VaapiPicture* picture);
 
   // Try to OutputPicture() if we have both a ready surface and picture.
diff --git a/media/gpu/vp8_decoder.cc b/media/gpu/vp8_decoder.cc
index 9d582ed..e7bdfc2e 100644
--- a/media/gpu/vp8_decoder.cc
+++ b/media/gpu/vp8_decoder.cc
@@ -94,6 +94,7 @@
   if (!curr_pic_)
     return kRanOutOfSurfaces;
 
+  curr_pic_->visible_rect = gfx::Rect(pic_size_);
   if (!DecodeAndOutputCurrentFrame())
     return kDecodeError;
 
diff --git a/media/gpu/vp8_picture.h b/media/gpu/vp8_picture.h
index 164cf0a..f31a344b 100644
--- a/media/gpu/vp8_picture.h
+++ b/media/gpu/vp8_picture.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/rect.h"
 
 namespace media {
 
@@ -20,6 +21,9 @@
   virtual V4L2VP8Picture* AsV4L2VP8Picture();
   virtual VaapiVP8Picture* AsVaapiVP8Picture();
 
+  // The visible size of picture.
+  gfx::Rect visible_rect;
+
  protected:
   friend class base::RefCounted<VP8Picture>;
   virtual ~VP8Picture();
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index b24f2466..be7fd85 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -138,6 +138,18 @@
     if (!pic)
       return kRanOutOfSurfaces;
 
+    gfx::Rect new_render_rect(curr_frame_hdr_->render_width,
+                              curr_frame_hdr_->render_height);
+    // For safety, check the validity of render size or leave it as (0, 0).
+    if (!gfx::Rect(pic_size_).Contains(new_render_rect)) {
+      DVLOG(1) << "Render size exceeds picture size. render size: "
+               << new_render_rect.ToString()
+               << ", picture size: " << pic_size_.ToString();
+      new_render_rect = gfx::Rect();
+    }
+    DVLOG(2) << "Render resolution: " << new_render_rect.ToString();
+
+    pic->visible_rect = new_render_rect;
     pic->frame_hdr.reset(curr_frame_hdr_.release());
 
     if (!DecodeAndOutputPicture(pic)) {
diff --git a/media/gpu/vp9_picture.h b/media/gpu/vp9_picture.h
index 7c026fa..ba95c88 100644
--- a/media/gpu/vp9_picture.h
+++ b/media/gpu/vp9_picture.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "media/filters/vp9_parser.h"
+#include "ui/gfx/geometry/rect.h"
 
 namespace media {
 
@@ -25,6 +26,11 @@
 
   std::unique_ptr<Vp9FrameHeader> frame_hdr;
 
+  // The visible size of picture. This could be either parsed from frame
+  // header, or set to gfx::Rect(0, 0) for indicating invalid values or
+  // not available.
+  gfx::Rect visible_rect;
+
  protected:
   friend class base::RefCounted<VP9Picture>;
   virtual ~VP9Picture();
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
index 6c7fcc5..fad3c8d 100644
--- a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
+++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
@@ -431,7 +431,7 @@
       fb.AppendZeroes(pad_length());
       VLOG(1) << "fb.size=" << fb.size();
       // Pick a random length for the payload that is shorter than neccesary.
-      payload_length = Random().Rand32() % fb.size();
+      payload_length = Random().Uniform(fb.size());
     }
 
     VLOG(1) << "payload_length=" << payload_length;
diff --git a/net/http2/hpack/decoder/hpack_decoder_state_test.cc b/net/http2/hpack/decoder/hpack_decoder_state_test.cc
index 0ed4098f..629370fe 100644
--- a/net/http2/hpack/decoder/hpack_decoder_state_test.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_state_test.cc
@@ -168,8 +168,8 @@
     const HpackStringPair* entry =
         Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
     VERIFY_NE(entry, nullptr);
-    VERIFY_EQ(static_cast<StringPiece>(entry->name), name);
-    VERIFY_EQ(static_cast<StringPiece>(entry->value), value);
+    VERIFY_EQ(entry->name.ToStringPiece(), name);
+    VERIFY_EQ(entry->value.ToStringPiece(), value);
     return AssertionSuccess();
   }
   AssertionResult VerifyNoEntry(size_t dynamic_index) {
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder_test.cc b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
index 7cdc872..088697d7 100644
--- a/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
+++ b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
@@ -36,7 +36,6 @@
 namespace {
 
 class HpackVarintDecoderTest : public RandomDecoderTest {
-
  protected:
   DecodeStatus StartDecoding(DecodeBuffer* b) override {
     CHECK_LT(0u, b->Remaining());
@@ -221,7 +220,7 @@
       // values that require exactly |expected_bytes| extension bytes.
       values.insert({start, start + 1, beyond - 2, beyond - 1});
       while (values.size() < 100) {
-        values.insert(start + Random().Rand32() % range);
+        values.insert(start + Random().Uniform(range));
       }
     }
 
diff --git a/net/http2/hpack/decoder/http2_hpack_decoder_test.cc b/net/http2/hpack/decoder/http2_hpack_decoder_test.cc
index 1d969b1..bbc81c44a 100644
--- a/net/http2/hpack/decoder/http2_hpack_decoder_test.cc
+++ b/net/http2/hpack/decoder/http2_hpack_decoder_test.cc
@@ -194,8 +194,8 @@
     const HpackStringPair* entry =
         Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
     VERIFY_NE(entry, nullptr);
-    VERIFY_EQ(static_cast<StringPiece>(entry->name), name);
-    VERIFY_EQ(static_cast<StringPiece>(entry->value), value);
+    VERIFY_EQ(entry->name.ToStringPiece(), name);
+    VERIFY_EQ(entry->value.ToStringPiece(), value);
     return AssertionSuccess();
   }
   AssertionResult VerifyNoEntry(size_t dynamic_index) {
diff --git a/net/http2/hpack/hpack_string.cc b/net/http2/hpack/hpack_string.cc
index b4b820b..1ed38e6 100644
--- a/net/http2/hpack/hpack_string.cc
+++ b/net/http2/hpack/hpack_string.cc
@@ -19,7 +19,7 @@
 HpackString::HpackString(const HpackString& other) : str_(other.str_) {}
 HpackString::~HpackString() {}
 
-HpackString::operator StringPiece() const {
+StringPiece HpackString::ToStringPiece() const {
   return str_;
 }
 
diff --git a/net/http2/hpack/hpack_string.h b/net/http2/hpack/hpack_string.h
index 5a7b693..04b95a95 100644
--- a/net/http2/hpack/hpack_string.h
+++ b/net/http2/hpack/hpack_string.h
@@ -36,7 +36,7 @@
 
   size_t size() const { return str_.size(); }
   const std::string& ToString() const { return str_; }
-  operator base::StringPiece() const;
+  base::StringPiece ToStringPiece() const;
 
   bool operator==(const HpackString& other) const;
 
diff --git a/net/http2/hpack/hpack_string_test.cc b/net/http2/hpack/hpack_string_test.cc
index b2181f8..337a3011 100644
--- a/net/http2/hpack/hpack_string_test.cc
+++ b/net/http2/hpack/hpack_string_test.cc
@@ -35,7 +35,7 @@
     VERIFY_NE(*actual, not_expected_ptr);
     VERIFY_NE(*actual, not_expected_sp);
     VERIFY_NE(*actual, not_expected_str);
-    VERIFY_NE(static_cast<StringPiece>(*actual), not_expected_sp);
+    VERIFY_NE(actual->ToStringPiece(), not_expected_sp);
 
     if (!(not_expected_ptr != *actual)) {
       return AssertionFailure();
@@ -46,7 +46,7 @@
     if (!(not_expected_str != *actual)) {
       return AssertionFailure();
     }
-    if (!(not_expected_sp != static_cast<StringPiece>(*actual))) {
+    if (!(not_expected_sp != actual->ToStringPiece())) {
       return AssertionFailure();
     }
 
@@ -62,7 +62,7 @@
     VERIFY_EQ(*actual, expected_ptr);
     VERIFY_EQ(*actual, expected_sp);
     VERIFY_EQ(*actual, expected_str);
-    VERIFY_EQ(static_cast<StringPiece>(*actual), expected_sp);
+    VERIFY_EQ(actual->ToStringPiece(), expected_sp);
 
     if (!(expected_sp == *actual)) {
       return AssertionFailure();
@@ -73,7 +73,7 @@
     if (!(expected_str == *actual)) {
       return AssertionFailure();
     }
-    if (!(expected_sp == static_cast<StringPiece>(*actual))) {
+    if (!(expected_sp == actual->ToStringPiece())) {
       return AssertionFailure();
     }
 
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index 2575ea5..6579764 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -2239,6 +2239,7 @@
       spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true));
 
   SpdyHeaderBlock push_headers;
+  push_headers[":method"] = "GET";
   spdy_util_.AddUrlToHeaderBlock(SpdyString(kDefaultUrl) + "b.dat",
                                  &push_headers);
 
@@ -3147,6 +3148,63 @@
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
+// Regression test for https://crbug.com/727653.
+TEST_F(SpdyNetworkTransactionTest, RejectServerPushWithNoMethod) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  SpdySerializedFrame rst(
+      spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM));
+  MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 3)};
+
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+
+  SpdyHeaderBlock push_promise_header_block;
+  spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat").c_str(),
+                                 &push_promise_header_block);
+  SpdyPushPromiseIR push_promise(1, 2, std::move(push_promise_header_block));
+  SpdySerializedFrame push_promise_frame(
+      spdy_util_.SerializeFrame(push_promise));
+
+  SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockRead reads[] = {
+      CreateMockRead(reply, 1), CreateMockRead(push_promise_frame, 2),
+      CreateMockRead(body, 4), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
+
+  SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
+                                     NetLogWithSource(), nullptr);
+  helper.RunToCompletion(&data);
+}
+
+// Regression test for https://crbug.com/727653.
+TEST_F(SpdyNetworkTransactionTest, RejectServerPushWithInvalidMethod) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  SpdySerializedFrame rst(
+      spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM));
+  MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 3)};
+
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+
+  SpdyHeaderBlock push_promise_header_block;
+  push_promise_header_block[":method"] = "POST";
+  spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat").c_str(),
+                                 &push_promise_header_block);
+  SpdyPushPromiseIR push_promise(1, 2, std::move(push_promise_header_block));
+  SpdySerializedFrame push_promise_frame(
+      spdy_util_.SerializeFrame(push_promise));
+
+  SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockRead reads[] = {
+      CreateMockRead(reply, 1), CreateMockRead(push_promise_frame, 2),
+      CreateMockRead(body, 4), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
+
+  SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
+                                     NetLogWithSource(), nullptr);
+  helper.RunToCompletion(&data);
+}
+
 // Verify that various response headers parse correctly through the HTTP layer.
 TEST_F(SpdyNetworkTransactionTest, ResponseHeaders) {
   struct ResponseHeadersTests {
@@ -4689,6 +4747,7 @@
       spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdyHeaderBlock initial_headers;
+  initial_headers[":method"] = "GET";
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
                                  &initial_headers);
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame(
@@ -4747,6 +4806,7 @@
   SpdySerializedFrame stream1_reply(
       spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdyHeaderBlock initial_headers;
+  initial_headers[":method"] = "GET";
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
                                  &initial_headers);
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame(
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index 1f251de6..cdf864b2 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -1665,6 +1665,19 @@
     return;
   }
 
+  // "Promised requests MUST be cacheable and MUST be safe [...]" (RFC7540
+  // Section 8.2).  Only cacheable safe request methods are GET and HEAD.
+  SpdyHeaderBlock::const_iterator it = headers.find(":method");
+  if (it == headers.end() ||
+      (it->second.compare("GET") != 0 && it->second.compare("HEAD") != 0)) {
+    EnqueueResetStreamFrame(
+        stream_id, request_priority, ERROR_CODE_REFUSED_STREAM,
+        SpdyStringPrintf(
+            "Rejected push stream %d due to inadequate request method",
+            associated_stream_id));
+    return;
+  }
+
   auto stream = base::MakeUnique<SpdyStream>(
       SPDY_PUSH_STREAM, GetWeakPtr(), gurl, request_priority,
       stream_initial_send_window_size_, stream_max_recv_window_size_, net_log_);
diff --git a/net/spdy/chromium/spdy_session_unittest.cc b/net/spdy/chromium/spdy_session_unittest.cc
index 4b601328..d141871 100644
--- a/net/spdy/chromium/spdy_session_unittest.cc
+++ b/net/spdy/chromium/spdy_session_unittest.cc
@@ -5222,6 +5222,7 @@
   SpdySerializedFrame push_a(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, "https://www.example.org/a.dat"));
   SpdyHeaderBlock push_headers;
+  push_headers[":method"] = "GET";
   spdy_util_.AddUrlToHeaderBlock("https://www.example.org/b.dat",
                                  &push_headers);
   SpdySerializedFrame push_b(
@@ -5316,6 +5317,7 @@
 TEST_F(SpdySessionTest, CancelReservedStreamOnHeadersReceived) {
   const char kPushedUrl[] = "https://www.example.org/a.dat";
   SpdyHeaderBlock push_headers;
+  push_headers[":method"] = "GET";
   spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_headers);
   SpdySerializedFrame push_promise(
       spdy_util_.ConstructInitialSpdyPushFrame(std::move(push_headers), 2, 1));
diff --git a/net/spdy/chromium/spdy_test_util_common.cc b/net/spdy/chromium/spdy_test_util_common.cc
index 9cefe6b..9da3d1c 100644
--- a/net/spdy/chromium/spdy_test_util_common.cc
+++ b/net/spdy/chromium/spdy_test_util_common.cc
@@ -873,6 +873,7 @@
     int associated_stream_id,
     const char* url) {
   SpdyHeaderBlock push_promise_header_block;
+  push_promise_header_block[GetMethodKey()] = "GET";
   AddUrlToHeaderBlock(url, &push_promise_header_block);
   SpdyPushPromiseIR push_promise(associated_stream_id, stream_id,
                                  std::move(push_promise_header_block));
@@ -899,6 +900,7 @@
     const char* status,
     const char* location) {
   SpdyHeaderBlock push_promise_header_block;
+  push_promise_header_block[GetMethodKey()] = "GET";
   AddUrlToHeaderBlock(url, &push_promise_header_block);
   SpdyPushPromiseIR push_promise(associated_stream_id, stream_id,
                                  std::move(push_promise_header_block));
diff --git a/net/spdy/core/hpack/hpack_decoder3.cc b/net/spdy/core/hpack/hpack_decoder3.cc
index c4472313..b7707676 100644
--- a/net/spdy/core/hpack/hpack_decoder3.cc
+++ b/net/spdy/core/hpack/hpack_decoder3.cc
@@ -143,10 +143,11 @@
   total_uncompressed_bytes_ += name.size() + value.size();
   if (handler_ == nullptr) {
     DVLOG(3) << "Adding to decoded_block";
-    decoded_block_.AppendValueOrAddHeader(name, value);
+    decoded_block_.AppendValueOrAddHeader(name.ToStringPiece(),
+                                          value.ToStringPiece());
   } else {
     DVLOG(3) << "Passing to handler";
-    handler_->OnHeader(name, value);
+    handler_->OnHeader(name.ToStringPiece(), value.ToStringPiece());
   }
 }
 
@@ -173,7 +174,8 @@
   if (visitor_ == nullptr) {
     return 0;
   }
-  HpackEntry entry(sp.name, sp.value, /*is_static*/ false, insert_count);
+  HpackEntry entry(sp.name.ToStringPiece(), sp.value.ToStringPiece(),
+                   /*is_static*/ false, insert_count);
   int64_t time_added = visitor_->OnNewEntry(entry);
   DVLOG(2) << "HpackDecoder3::ListenerAdapter::OnEntryInserted: time_added="
            << time_added;
@@ -187,7 +189,8 @@
            << ",  insert_count=" << insert_count
            << ",  time_added=" << time_added;
   if (visitor_ != nullptr) {
-    HpackEntry entry(sp.name, sp.value, /*is_static*/ false, insert_count);
+    HpackEntry entry(sp.name.ToStringPiece(), sp.value.ToStringPiece(),
+                     /*is_static*/ false, insert_count);
     entry.set_time_added(time_added);
     visitor_->OnUseEntry(entry);
   }
diff --git a/net/spdy/core/hpack/hpack_decoder3_test.cc b/net/spdy/core/hpack/hpack_decoder3_test.cc
index 223098caa..a1ff3d71 100644
--- a/net/spdy/core/hpack/hpack_decoder3_test.cc
+++ b/net/spdy/core/hpack/hpack_decoder3_test.cc
@@ -137,7 +137,7 @@
         // Decode some fragment of the remaining bytes.
         size_t bytes = str.length();
         if (!str.empty()) {
-          bytes = (random_.Rand8() % str.length()) + 1;
+          bytes = random_.Uniform(str.length()) + 1;
         }
         EXPECT_LE(bytes, str.length());
         if (!HandleControlFrameHeadersData(str.substr(0, bytes))) {
diff --git a/net/spdy/core/spdy_protocol.cc b/net/spdy/core/spdy_protocol.cc
index f12b6371..89b9c9541 100644
--- a/net/spdy/core/spdy_protocol.cc
+++ b/net/spdy/core/spdy_protocol.cc
@@ -386,9 +386,7 @@
 }
 
 SpdyFrameType SpdyUnknownIR::frame_type() const {
-  // TODO(birenroy): Remove the fake EXTENSION value from the SpdyFrameType
-  // enum.
-  return SpdyFrameType::EXTENSION;
+  return static_cast<SpdyFrameType>(type());
 }
 
 }  // namespace net
diff --git a/net/spdy/core/spdy_protocol.h b/net/spdy/core/spdy_protocol.h
index c4111a8..d40f6fb 100644
--- a/net/spdy/core/spdy_protocol.h
+++ b/net/spdy/core/spdy_protocol.h
@@ -88,6 +88,8 @@
   // The specific value of EXTENSION is meaningless; it is a placeholder used
   // within SpdyFramer's state machine when handling unknown frames via an
   // extension API.
+  // TODO(birenroy): Remove the fake EXTENSION value from the SpdyFrameType
+  // enum.
   EXTENSION = 0xff
 };
 
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 89224b7f..bde8b9f 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -216,6 +216,10 @@
 #define SK_SUPPORT_LEGACY_GRADIENT_ALPHATRUNC
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_DITHERING
+#define SK_SUPPORT_LEGACY_DITHERING
+#endif
+
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index b48b43c6..47df20a4 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -15698,6 +15698,8 @@
 crbug.com/591099 http/tests/media/autoplay-crossorigin.html [ Crash ]
 crbug.com/591099 http/tests/media/controls/controls-list-add-hide.html [ Crash ]
 crbug.com/591099 http/tests/media/controls/controls-list-remove-show.html [ Crash ]
+crbug.com/591099 http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html [ Crash ]
+crbug.com/591099 http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html [ Crash ]
 crbug.com/591099 http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Crash ]
 crbug.com/591099 http/tests/media/encrypted-media/encrypted-media-encrypted-event-same-origin.html [ Crash ]
 crbug.com/591099 http/tests/media/media-source/mediasource-addsourcebuffer.html [ Crash ]
@@ -15759,8 +15761,6 @@
 crbug.com/591099 http/tests/media/video-controls-download-button-not-displayed-hide-download-ui.html [ Crash ]
 crbug.com/591099 http/tests/media/video-controls-download-button-not-displayed-mediastream.html [ Crash ]
 crbug.com/591099 http/tests/media/video-controls-download-button-not-displayed-mse.html [ Crash ]
-crbug.com/591099 http/tests/media/video-controls-overflow-menu-correct-ordering.html [ Crash ]
-crbug.com/591099 http/tests/media/video-controls-overflow-menu-updates-appropriately.html [ Crash ]
 crbug.com/591099 http/tests/media/video-cookie.html [ Crash ]
 crbug.com/591099 http/tests/media/video-error-abort.html [ Crash ]
 crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Crash ]
@@ -23026,6 +23026,10 @@
 crbug.com/591099 virtual/mojo-localstorage/storage/domstorage/localstorage/missing-arguments.html [ Failure ]
 crbug.com/591099 virtual/mse-1mb-buffers/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-1mb-buffers.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/external/wpt/remote-playback/idlharness.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/http/tests/media/controls/controls-list-add-hide.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/http/tests/media/controls/controls-list-remove-show.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/closed-captions-dynamic-update.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/closed-captions-on-off.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/closed-captions-single-track.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index 9912f19..f3c1c55 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -2162,6 +2162,8 @@
 Bug(none) http/tests/media/autoplay-crossorigin.html [ Timeout ]
 Bug(none) http/tests/media/controls/controls-list-add-hide.html [ Timeout ]
 Bug(none) http/tests/media/controls/controls-list-remove-show.html [ Timeout ]
+Bug(none) http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html [ Timeout ]
+Bug(none) http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html [ Timeout ]
 Bug(none) http/tests/media/encrypted-media/encrypted-media-encrypted-event-same-origin.html [ Timeout ]
 Bug(none) http/tests/media/gc-while-network-loading.html [ Timeout ]
 Bug(none) http/tests/media/media-source/mediasource-addsourcebuffer.html [ Timeout ]
@@ -2207,9 +2209,7 @@
 Bug(none) http/tests/media/video-controls-download-button-not-displayed-hide-download-ui.html [ Timeout ]
 Bug(none) http/tests/media/video-controls-download-button-not-displayed-mediastream.html [ Timeout ]
 Bug(none) http/tests/media/video-controls-download-button-saves-media.html [ Timeout ]
-Bug(none) http/tests/media/video-controls-overflow-menu-correct-ordering.html [ Timeout ]
 Bug(none) http/tests/media/video-controls-overflow-menu-download-button.html [ Timeout ]
-Bug(none) http/tests/media/video-controls-overflow-menu-updates-appropriately.html [ Timeout ]
 Bug(none) http/tests/media/video-cookie.html [ Timeout ]
 Bug(none) http/tests/media/video-error-abort.html [ Timeout ]
 Bug(none) http/tests/media/video-in-iframe-crash.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 80196b95..62d60ea 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1680,9 +1680,6 @@
 
 crbug.com/572723 inspector/sources/debugger/debugger-uncaught-promise-on-pause.html [ Timeout Pass ]
 
-crbug.com/634264 http/tests/security/xss-DENIED-cross-origin-stack-overflow.html [ Crash Timeout ]
-crbug.com/634264 virtual/mojo-loading/http/tests/security/xss-DENIED-cross-origin-stack-overflow.html [ Crash Timeout ]
-
 crbug.com/594672 fast/events/iframe-object-onload.html [ Failure Pass ]
 crbug.com/594672 fast/events/scale-and-scroll-iframe-body.html [ Failure Pass ]
 crbug.com/594672 fast/events/updateLayoutForHitTest.html [ Failure Pass ]
@@ -2755,8 +2752,6 @@
 crbug.com/731018 [ Mac ] sensor/magnetometer.html [ Failure Pass ]
 crbug.com/731018 [ Mac ] sensor/orientation-sensor.html [ Failure Pass ]
 
-crbug.com/731035 [ Linux Win Mac ] virtual/new-remote-playback-pipeline/media/controls/video-controls-overflow-menu-hide-on-click-panel.html [ Failure Pass ]
-
 # More flaky tests on Mac
 crbug.com/731111 [ Mac ] http/tests/media/progress-events-generated-correctly.html [ Failure Pass Timeout ]
 crbug.com/731111 [ Mac ] http/tests/media/video-load-metadata-decode-error.html [ Failure Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 105225b..32ef585 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -496,6 +496,11 @@
     "args": ["--enable-features=NewRemotePlaybackPipeline"]
   },
   {
+    "prefix": "new-remote-playback-pipeline",
+    "base": "http/tests/media/controls",
+    "args": ["--enable-features=NewRemotePlaybackPipeline"]
+  },
+  {
     "prefix": "high-contrast-mode",
     "base": "paint/high-contrast-mode/image-filter-all",
     "args": ["--blink-settings=highContrastMode=3,highContrastImagePolicy=0"]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/frame-src/frame-src-self-unique-origin.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/frame-src/frame-src-self-unique-origin.html
new file mode 100644
index 0000000..947b11e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/frame-src/frame-src-self-unique-origin.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>frame-src-self-unique-origin</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+    <p>
+        The origin of an URL is called "unique" when it is considered to be
+        different from every origin, including itself. The origin of a
+        data-url is unique. When the current origin is unique, the CSP source
+        'self' must not match any URL.
+    </p>
+    <script>
+        var iframe = document.createElement("iframe");
+        iframe.src = encodeURI(`data:text/html,
+          <script>
+              /* Add the CSP: frame-src: 'self'. */
+              var meta = document.createElement('meta');
+              meta.httpEquiv = 'Content-Security-Policy';
+              meta.content = "frame-src 'self'";
+              document.getElementsByTagName('head')[0].appendChild(meta);
+
+              /* Notify the parent the iframe has been blocked. */
+              window.addEventListener('securitypolicyviolation', e => {
+                  if (e.originalPolicy == "frame-src 'self'")
+                      window.parent.postMessage('Test PASS', '*');
+              });
+          </scr`+`ipt>
+
+          This iframe should be blocked by CSP:
+          <iframe src='data:text/html,blocked_iframe'></iframe>
+        `);
+        if (window.async_test) {
+            async_test(t => { 
+                window.addEventListener("message", e => {
+                    if (e.data == "Test PASS")
+                      t.done();
+                });
+            }, "Iframe's url must not match with 'self'. It must be blocked.");
+        }
+        document.body.appendChild(iframe);
+    </script>
+</body>
+
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/img-src/img-src-self-unique-origin.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/img-src/img-src-self-unique-origin.html
new file mode 100644
index 0000000..1fd2869b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/img-src/img-src-self-unique-origin.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>img-src-self-unique-origin</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+    <p>
+        The origin of an URL is called "unique" when it is considered to be
+        different from every origin, including itself. The origin of a
+        data-url is unique. When the current origin is unique, the CSP source
+        'self' must not match any URL.
+    </p>
+    <script>
+        var iframe = document.createElement("iframe");
+        iframe.src = encodeURI(`data:text/html,
+          <script>
+              /* Add the CSP: frame-src: 'self'. */
+              var meta = document.createElement('meta');
+              meta.httpEquiv = 'Content-Security-Policy';
+              meta.content = "img-src 'self'";
+              document.getElementsByTagName('head')[0].appendChild(meta);
+
+              /* Notify the parent the image has been blocked. */
+              window.addEventListener('securitypolicyviolation', e => {
+                  if (e.originalPolicy == "img-src 'self'")
+                      window.parent.postMessage('Test PASS', '*');
+              });
+          </scr`+`ipt>
+
+          This image should be blocked by CSP:
+          <img src='data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'></img>
+        `);
+        if (window.async_test) {
+            async_test(t => { 
+                window.addEventListener("message", e => {
+                    if (e.data == "Test PASS")
+                      t.done();
+                });
+            }, "Image's url must not match with 'self'. Image must be blocked.");
+        }
+        document.body.appendChild(iframe);
+    </script>
+</body>
+
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/remote-playback/cancel-watch-availability.html b/third_party/WebKit/LayoutTests/external/wpt/remote-playback/cancel-watch-availability.html
index 99cae8e..e4b6330 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/remote-playback/cancel-watch-availability.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/remote-playback/cancel-watch-availability.html
@@ -23,7 +23,7 @@
 
 async_test(t => {
   var v = document.createElement('video');
-  v.src = getVideoURI('media_5');
+  v.src = getVideoURI('movie_5');
 
   Promise.all([
       v.remote.watchAvailability(function() {}),
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-correct-ordering.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-correct-ordering.html
rename to third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html
index b9eca1e..3aa5fa3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-correct-ordering.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-correct-ordering.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <title>Overflow menu children appear in correct order.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../../media-resources/media-controls.js"></script>
-<script src="../../media-resources/media-file.js"></script>
-<script src="../../media-resources/overflow-menu.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../../media-resources/media-controls.js"></script>
+<script src="../../../media-resources/media-file.js"></script>
+<script src="../../../media-resources/overflow-menu.js"></script>
 
 <!--Padding ensures the overflow menu is visible for the tests. -->
 <body style="padding-top: 200px; padding-left: 100px">
@@ -13,7 +13,7 @@
 async_test(function(t) {
   // Set up video
   var video = document.querySelector("video");
-  video.src = findMediaFile("video", "resources/test");
+  video.src = findMediaFile("video", "../resources/test");
   video.setAttribute("width", "60");
   // Add captions
   var track = video.addTextTrack("captions");
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html
rename to third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html
index a4ace4f..8b1f18e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-controls-overflow-menu-updates-appropriately.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/controls/video-controls-overflow-menu-updates-appropriately.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <title>Overflow menu updates properly.</title>
-<script src='../resources/testharness.js'></script>
-<script src='../resources/testharnessreport.js'></script>
-<script src='../../media-resources/media-controls.js'></script>
-<script src='../../media-resources/media-file.js'></script>
-<script src='../../media-resources/overflow-menu.js'></script>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../../../media-resources/media-controls.js'></script>
+<script src='../../../media-resources/media-file.js'></script>
+<script src='../../../media-resources/overflow-menu.js'></script>
 
 <!--Padding ensures the overflow menu is visible for the tests. -->
 <body style='padding-top: 200px; padding-left: 100px'>
@@ -15,7 +15,7 @@
 
   // Set up video
   var video = document.querySelector('video');
-  video.src = findMediaFile('video', 'resources/test');
+  video.src = findMediaFile('video', '../resources/test');
   video.setAttribute('width', '60');
   // Add captions
   var trackElement = document.createElement('track');
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/xss-DENIED-cross-origin-stack-overflow.html b/third_party/WebKit/LayoutTests/http/tests/security/xss-DENIED-cross-origin-stack-overflow.html
index 35e6d11..d489cf0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/xss-DENIED-cross-origin-stack-overflow.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/xss-DENIED-cross-origin-stack-overflow.html
@@ -13,8 +13,8 @@
     var win = iframe.contentWindow;
     function recurse() {
         try { recurse(); } catch(e) {}
-        try { win.location; } catch(e) { 
-            o = e; 
+        try { win.location.href; } catch(e) {
+            o = e;
         }
     }
     recurse();
diff --git a/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html b/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html
new file mode 100644
index 0000000..a6935363
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/mojo-helpers.js"></script>
+<script src="resources/mock-imagecapture.js"></script>
+<body>
+<canvas id='canvas' width=10 height=10/>
+</body>
+<script>
+
+const fillLightModeNames = ["off", "auto", "flash"];
+
+// This test verifies that ImageCapture can call getPhotoSettings(), with a
+// mock Mojo interface implementation.
+async_test(function(t) {
+  var canvas = document.getElementById('canvas');
+  var context = canvas.getContext("2d");
+  context.fillStyle = "red";
+  context.fillRect(0, 0, 10, 10);
+  var stream = canvas.captureStream();
+
+  var mock_state;
+
+  mockImageCaptureReady
+    .then(mock => {
+      mock_state = mock.state();
+      return new ImageCapture(stream.getVideoTracks()[0]);
+    },
+    error => {
+      assert_unreached("Error creating MockImageCapture: " + error);
+    })
+    .then(capturer => {
+      return capturer.getPhotoSettings();
+    })
+    .then(settings => {
+      assert_equals(settings.imageWidth, mock_state.width.current, 'width');
+      assert_equals(settings.imageHeight, mock_state.height.current, 'height');
+      // TODO(mcasas): check the remaining two entries https://crbug.com/732521.
+
+      t.done();
+    })
+    .catch(error => {
+      assert_unreached("Error during getPhotoSettings(): " + error.message);
+    });
+}, 'exercises ImageCapture.getPhotoSettings()');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/availability-callback-gc.html b/third_party/WebKit/LayoutTests/media/remoteplayback/availability-callback-gc.html
index 873ea3f..c4135ae2 100644
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/availability-callback-gc.html
+++ b/third_party/WebKit/LayoutTests/media/remoteplayback/availability-callback-gc.html
@@ -12,7 +12,7 @@
             async_test(function(t)
             {
                 var v = document.createElement('video');
-                v.src = findMediaFile('video', 'content/test');
+                v.src = findMediaFile('video', '../content/test');
                 document.body.appendChild(v);
 
                 function callback(available) {}
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-throws-low-end-device.html b/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-throws-low-end-device.html
index 2e331ff7..87579191 100644
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-throws-low-end-device.html
+++ b/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-throws-low-end-device.html
@@ -13,7 +13,7 @@
             async_test(function(t)
             {
                 var v = document.createElement('video');
-                v.src = findMediaFile('video', 'content/test');
+                v.src = findMediaFile('video', '../content/test');
                 document.body.appendChild(v);
                 internals.setIsLowEndDevice(true);
                 v.remote.watchAvailability(t.unreached_func()).then(
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
index b743349..30a219a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3157,6 +3157,7 @@
     getter track
     method constructor
     method getPhotoCapabilities
+    method getPhotoSettings
     method grabFrame
     method setOptions
     method takePhoto
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index ff81a8a..720f354 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3086,6 +3086,7 @@
     getter track
     method constructor
     method getPhotoCapabilities
+    method getPhotoSettings
     method grabFrame
     method setOptions
     method takePhoto
diff --git a/third_party/WebKit/LayoutTests/virtual/new-remote-playback-pipeline/http/tests/media/controls/README.txt b/third_party/WebKit/LayoutTests/virtual/new-remote-playback-pipeline/http/tests/media/controls/README.txt
new file mode 100644
index 0000000..261bd72
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/new-remote-playback-pipeline/http/tests/media/controls/README.txt
@@ -0,0 +1,2 @@
+The new-remote-playback-pipeline enables the new remote playback pipeline to run
+the existing native controls tests against the new pipeline.
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index 9b7e867..44f56e6 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -3819,6 +3819,7 @@
     getter track
     method constructor
     method getPhotoCapabilities
+    method getPhotoSettings
     method grabFrame
     method setOptions
     method takePhoto
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index f1136b1..ae8e3a87 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -3819,6 +3819,7 @@
     getter track
     method constructor
     method getPhotoCapabilities
+    method getPhotoSettings
     method grabFrame
     method setOptions
     method takePhoto
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 768644e0..28999ea 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1243,6 +1243,8 @@
     "events/EventTargetTest.cpp",
     "events/PointerEventFactoryTest.cpp",
     "events/TouchEventTest.cpp",
+    "exported/WebAssociatedURLLoaderImplTest.cpp",
+    "exported/WebNodeTest.cpp",
     "fileapi/FileListTest.cpp",
     "fileapi/FileTest.cpp",
     "frame/DOMTimerTest.cpp",
diff --git a/third_party/WebKit/Source/core/dom/AXObject.cpp b/third_party/WebKit/Source/core/dom/AXObject.cpp
index 5bd21075..b0252e1 100644
--- a/third_party/WebKit/Source/core/dom/AXObject.cpp
+++ b/third_party/WebKit/Source/core/dom/AXObject.cpp
@@ -7,6 +7,7 @@
 #include "core/HTMLElementTypeHelpers.h"
 #include "core/dom/Element.h"
 #include "core/dom/Node.h"
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/text/StringHash.h"
 #include "platform/wtf/text/WTFString.h"
@@ -97,10 +98,6 @@
   return false;
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
-
 STATIC_ASSERT_ENUM(kWebAXRoleAbbr, kAbbrRole);
 STATIC_ASSERT_ENUM(kWebAXRoleAlertDialog, kAlertDialogRole);
 STATIC_ASSERT_ENUM(kWebAXRoleAlert, kAlertRole);
diff --git a/third_party/WebKit/Source/core/dom/AXObjectCache.cpp b/third_party/WebKit/Source/core/dom/AXObjectCache.cpp
index 57c2948..ce8757e 100644
--- a/third_party/WebKit/Source/core/dom/AXObjectCache.cpp
+++ b/third_party/WebKit/Source/core/dom/AXObjectCache.cpp
@@ -29,6 +29,7 @@
 #include "core/dom/AXObjectCache.h"
 
 #include <memory>
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/web/WebAXEnums.h"
 
@@ -75,10 +76,6 @@
   return cache;
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
-
 STATIC_ASSERT_ENUM(kWebAXEventActiveDescendantChanged,
                    AXObjectCache::kAXActiveDescendantChanged);
 STATIC_ASSERT_ENUM(kWebAXEventAlert, AXObjectCache::kAXAlert);
diff --git a/third_party/WebKit/Source/core/editing/TextAffinity.cpp b/third_party/WebKit/Source/core/editing/TextAffinity.cpp
index 7f4b8c21..f159d96 100644
--- a/third_party/WebKit/Source/core/editing/TextAffinity.cpp
+++ b/third_party/WebKit/Source/core/editing/TextAffinity.cpp
@@ -5,6 +5,7 @@
 #include "core/editing/TextAffinity.h"
 
 #include <ostream>  // NOLINT
+#include "platform/wtf/Assertions.h"
 #include "public/web/WebAXEnums.h"
 
 namespace blink {
@@ -19,9 +20,6 @@
   return ostream << "TextAffinity(" << static_cast<int>(affinity) << ')';
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
 
 STATIC_ASSERT_ENUM(kWebAXTextAffinityUpstream, TextAffinity::kUpstream);
 STATIC_ASSERT_ENUM(kWebAXTextAffinityDownstream, TextAffinity::kDownstream);
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp
index e443834..7f409d9 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp
@@ -31,6 +31,7 @@
 #include "core/editing/markers/DocumentMarker.h"
 
 #include "core/editing/markers/TextMatchMarker.h"
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/StdLibExtras.h"
 #include "public/web/WebAXEnums.h"
 
@@ -94,10 +95,6 @@
   end_offset_ += delta;
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
-
 STATIC_ASSERT_ENUM(kWebAXMarkerTypeSpelling, DocumentMarker::kSpelling);
 STATIC_ASSERT_ENUM(kWebAXMarkerTypeGrammar, DocumentMarker::kGrammar);
 STATIC_ASSERT_ENUM(kWebAXMarkerTypeTextMatch, DocumentMarker::kTextMatch);
diff --git a/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp b/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
index e21e3706..9cb72c1 100644
--- a/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImpl.cpp
@@ -46,6 +46,7 @@
 #include "platform/loader/fetch/ResourceError.h"
 #include "platform/loader/fetch/ResourceLoaderOptions.h"
 #include "platform/network/HTTPParsers.h"
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/text/WTFString.h"
@@ -348,10 +349,6 @@
   Cancel();
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
-
 STATIC_ASSERT_ENUM(WebAssociatedURLLoaderOptions::kConsiderPreflight,
                    kConsiderPreflight);
 STATIC_ASSERT_ENUM(WebAssociatedURLLoaderOptions::kPreventPreflight,
diff --git a/third_party/WebKit/Source/web/WebAssociatedURLLoaderImplTest.cpp b/third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImplTest.cpp
similarity index 100%
rename from third_party/WebKit/Source/web/WebAssociatedURLLoaderImplTest.cpp
rename to third_party/WebKit/Source/core/exported/WebAssociatedURLLoaderImplTest.cpp
diff --git a/third_party/WebKit/Source/web/WebNodeTest.cpp b/third_party/WebKit/Source/core/exported/WebNodeTest.cpp
similarity index 99%
rename from third_party/WebKit/Source/web/WebNodeTest.cpp
rename to third_party/WebKit/Source/core/exported/WebNodeTest.cpp
index e00391d..fb55724 100644
--- a/third_party/WebKit/Source/web/WebNodeTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebNodeTest.cpp
@@ -4,13 +4,13 @@
 
 #include "public/web/WebNode.h"
 
+#include <memory>
 #include "core/dom/Document.h"
 #include "core/dom/Element.h"
 #include "core/testing/DummyPageHolder.h"
 #include "public/web/WebElement.h"
 #include "public/web/WebElementCollection.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include <memory>
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
index 136f83c4..4bf34611 100644
--- a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
+++ b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
@@ -20,6 +20,7 @@
 #include "core/page/FocusController.h"
 #include "core/page/Page.h"
 #include "core/page/PointerLockController.h"
+#include "platform/wtf/Assertions.h"
 #include "public/web/WebWidgetClient.h"
 
 namespace blink {
@@ -36,9 +37,6 @@
 
 // Ensure that the WebDragOperation enum values stay in sync with the original
 // DragOperation constants.
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum : " #a)
 STATIC_ASSERT_ENUM(kDragOperationNone, kWebDragOperationNone);
 STATIC_ASSERT_ENUM(kDragOperationCopy, kWebDragOperationCopy);
 STATIC_ASSERT_ENUM(kDragOperationLink, kWebDragOperationLink);
diff --git a/third_party/WebKit/Source/modules/exported/BUILD.gn b/third_party/WebKit/Source/modules/exported/BUILD.gn
index 3713673..f58d39c 100644
--- a/third_party/WebKit/Source/modules/exported/BUILD.gn
+++ b/third_party/WebKit/Source/modules/exported/BUILD.gn
@@ -26,3 +26,24 @@
 
   defines = [ "BLINK_MODULES_IMPLEMENTATION=1" ]
 }
+
+static_library("test_support") {
+  deps = [
+    "//skia",
+    "//third_party/WebKit/Source/core:testing",
+    "//third_party/WebKit/Source/modules:modules_testing",
+    "//third_party/WebKit/Source/platform/wtf",
+    "//v8",
+  ]
+
+  sources = [
+    "WebTestingSupport.cpp",
+  ]
+
+  configs += [
+    "//third_party/WebKit/Source:config",
+    "//third_party/WebKit/Source/core:blink_core_pch",
+  ]
+
+  include_dirs = [ "$root_gen_dir/blink" ]
+}
diff --git a/third_party/WebKit/Source/web/WebTestingSupport.cpp b/third_party/WebKit/Source/modules/exported/WebTestingSupport.cpp
similarity index 100%
rename from third_party/WebKit/Source/web/WebTestingSupport.cpp
rename to third_party/WebKit/Source/modules/exported/WebTestingSupport.cpp
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index 1fabf58..fa0cd82 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -13,7 +13,6 @@
 #include "modules/EventTargetModules.h"
 #include "modules/imagecapture/MediaSettingsRange.h"
 #include "modules/imagecapture/PhotoCapabilities.h"
-#include "modules/imagecapture/PhotoSettings.h"
 #include "modules/mediastream/MediaStreamTrack.h"
 #include "modules/mediastream/MediaTrackCapabilities.h"
 #include "modules/mediastream/MediaTrackConstraints.h"
@@ -128,7 +127,10 @@
     resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
     return promise;
   }
-  service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+  service_requests_.insert(resolver);
+
+  auto resolver_cb = WTF::Bind(&ImageCapture::ResolveWithPhotoCapabilities,
+                               WrapPersistent(this));
 
   // m_streamTrack->component()->source()->id() is the renderer "name" of the
   // camera;
@@ -136,9 +138,36 @@
   // scriptState->getExecutionContext()->getSecurityOrigin()->toString()
   service_->GetPhotoState(
       stream_track_->Component()->Source()->Id(),
-      ConvertToBaseCallback(
-          WTF::Bind(&ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
-                    WrapPersistent(resolver), false /* trigger_take_photo */)));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+          WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+          false /* trigger_take_photo */)));
+  return promise;
+}
+
+ScriptPromise ImageCapture::getPhotoSettings(ScriptState* script_state) {
+  ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
+  ScriptPromise promise = resolver->Promise();
+
+  if (!service_) {
+    resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
+    return promise;
+  }
+  service_requests_.insert(resolver);
+
+  auto resolver_cb =
+      WTF::Bind(&ImageCapture::ResolveWithPhotoSettings, WrapPersistent(this));
+
+  // m_streamTrack->component()->source()->id() is the renderer "name" of the
+  // camera;
+  // TODO(mcasas) consider sending the security origin as well:
+  // scriptState->getExecutionContext()->getSecurityOrigin()->toString()
+  service_->GetPhotoState(
+      stream_track_->Component()->Source()->Id(),
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+          WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+          false /* trigger_take_photo */)));
   return promise;
 }
 
@@ -158,7 +187,7 @@
     resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
     return promise;
   }
-  service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+  service_requests_.insert(resolver);
 
   // TODO(mcasas): should be using a mojo::StructTraits instead.
   auto settings = media::mojom::blink::PhotoSettings::New();
@@ -211,11 +240,15 @@
     settings->fill_light_mode = ParseFillLightMode(fill_light_mode);
   }
 
+  auto resolver_cb =
+      WTF::Bind(&ImageCapture::ResolveWithNothing, WrapPersistent(this));
+
   service_->SetOptions(
       stream_track_->Component()->Source()->Id(), std::move(settings),
       ConvertToBaseCallback(
           WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
-                    WrapPersistent(resolver), trigger_take_photo)));
+                    WrapPersistent(resolver),
+                    WTF::Passed(std::move(resolver_cb)), trigger_take_photo)));
   return promise;
 }
 
@@ -233,7 +266,7 @@
     return promise;
   }
 
-  service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+  service_requests_.insert(resolver);
 
   // m_streamTrack->component()->source()->id() is the renderer "name" of the
   // camera;
@@ -499,13 +532,19 @@
 
   current_constraints_ = temp_constraints;
 
-  service_requests_.insert(resolver, constraints_vector);
+  service_requests_.insert(resolver);
+
+  MediaTrackConstraints resolver_constraints;
+  resolver_constraints.setAdvanced(constraints_vector);
+  auto resolver_cb = WTF::Bind(&ImageCapture::ResolveWithMediaTrackConstraints,
+                               WrapPersistent(this), resolver_constraints);
 
   service_->SetOptions(
       stream_track_->Component()->Source()->Id(), std::move(settings),
-      ConvertToBaseCallback(
-          WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
-                    WrapPersistent(resolver), false /* trigger_take_photo */)));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoSetOptions, WrapPersistent(this),
+          WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+          false /* trigger_take_photo */)));
 }
 
 const MediaTrackConstraintSet& ImageCapture::GetMediaTrackConstraints() const {
@@ -599,10 +638,10 @@
 
 void ImageCapture::OnMojoGetPhotoState(
     ScriptPromiseResolver* resolver,
+    PromiseResolverFunction resolve_function,
     bool trigger_take_photo,
     media::mojom::blink::PhotoStatePtr photo_state) {
-  if (!service_requests_.Contains(resolver))
-    return;
+  DCHECK(service_requests_.Contains(resolver));
 
   if (photo_state.is_null()) {
     resolver->Reject(DOMException::Create(kUnknownError, "platform error"));
@@ -610,6 +649,11 @@
     return;
   }
 
+  photo_settings_ = PhotoSettings();
+  photo_settings_.setImageHeight(photo_state->height->current);
+  photo_settings_.setImageWidth(photo_state->width->current);
+  // TODO(mcasas): collect the remaining two entries https://crbug.com/732521.
+
   photo_capabilities_ = PhotoCapabilities::Create();
   photo_capabilities_->SetRedEyeReduction(photo_state->red_eye_reduction);
   // TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
@@ -637,27 +681,15 @@
     return;
   }
 
-  // If this is a response to a SetMediaTrackConstraints() request, it will have
-  // the original constraints to apply: Resolve() with those; Resolve() with the
-  // |photo_capabilities_| otherwise.
-  const HeapVector<MediaTrackConstraintSet>& originalConstraints =
-      service_requests_.at(resolver);
-  if (originalConstraints.IsEmpty()) {
-    resolver->Resolve(photo_capabilities_);
-  } else {
-    MediaTrackConstraints constraints;
-    constraints.setAdvanced(originalConstraints);
-    resolver->Resolve(constraints);
-  }
-
+  (*resolve_function)(resolver);
   service_requests_.erase(resolver);
 }
 
 void ImageCapture::OnMojoSetOptions(ScriptPromiseResolver* resolver,
+                                    PromiseResolverFunction resolve_function,
                                     bool trigger_take_photo,
                                     bool result) {
-  if (!service_requests_.Contains(resolver))
-    return;
+  DCHECK(service_requests_.Contains(resolver));
 
   if (!result) {
     resolver->Reject(DOMException::Create(kUnknownError, "setOptions failed"));
@@ -668,15 +700,15 @@
   // Retrieve the current device status after setting the options.
   service_->GetPhotoState(
       stream_track_->Component()->Source()->Id(),
-      ConvertToBaseCallback(
-          WTF::Bind(&ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
-                    WrapPersistent(resolver), trigger_take_photo)));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+          WrapPersistent(resolver), WTF::Passed(std::move(resolve_function)),
+          trigger_take_photo)));
 }
 
 void ImageCapture::OnMojoTakePhoto(ScriptPromiseResolver* resolver,
                                    media::mojom::blink::BlobPtr blob) {
-  if (!service_requests_.Contains(resolver))
-    return;
+  DCHECK(service_requests_.Contains(resolver));
 
   // TODO(mcasas): Should be using a mojo::StructTraits.
   if (blob->data.IsEmpty()) {
@@ -790,11 +822,34 @@
 
 void ImageCapture::OnServiceConnectionError() {
   service_.reset();
-  for (const auto& resolver : service_requests_)
-    resolver.key->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
+  for (ScriptPromiseResolver* resolver : service_requests_)
+    resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
   service_requests_.clear();
 }
 
+void ImageCapture::ResolveWithNothing(ScriptPromiseResolver* resolver) {
+  DCHECK(resolver);
+  resolver->Resolve();
+}
+
+void ImageCapture::ResolveWithPhotoSettings(ScriptPromiseResolver* resolver) {
+  DCHECK(resolver);
+  resolver->Resolve(photo_settings_);
+}
+
+void ImageCapture::ResolveWithPhotoCapabilities(
+    ScriptPromiseResolver* resolver) {
+  DCHECK(resolver);
+  resolver->Resolve(photo_capabilities_);
+}
+
+void ImageCapture::ResolveWithMediaTrackConstraints(
+    MediaTrackConstraints constraints,
+    ScriptPromiseResolver* resolver) {
+  DCHECK(resolver);
+  resolver->Resolve(constraints);
+}
+
 DEFINE_TRACE(ImageCapture) {
   visitor->Trace(stream_track_);
   visitor->Trace(capabilities_);
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
index 1c096df1..600144c8 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
@@ -12,6 +12,7 @@
 #include "media/capture/mojo/image_capture.mojom-blink.h"
 #include "modules/EventTargetModules.h"
 #include "modules/ModulesExport.h"
+#include "modules/imagecapture/PhotoSettings.h"
 #include "modules/mediastream/MediaTrackCapabilities.h"
 #include "modules/mediastream/MediaTrackConstraintSet.h"
 #include "modules/mediastream/MediaTrackSettings.h"
@@ -24,11 +25,10 @@
 class MediaStreamTrack;
 class MediaTrackConstraints;
 class PhotoCapabilities;
-class PhotoSettings;
 class ScriptPromiseResolver;
 class WebImageCaptureFrameGrabber;
 
-// TODO(mcasas): Consideradding a LayoutTest checking that this class is not
+// TODO(mcasas): Consider adding a LayoutTest checking that this class is not
 // garbage collected while it has event listeners.
 class MODULES_EXPORT ImageCapture final
     : public EventTargetWithInlineData,
@@ -56,6 +56,7 @@
   MediaStreamTrack* videoStreamTrack() const { return stream_track_.Get(); }
 
   ScriptPromise getPhotoCapabilities(ScriptState*);
+  ScriptPromise getPhotoSettings(ScriptState*);
 
   ScriptPromise setOptions(ScriptState*,
                            const PhotoSettings&,
@@ -79,12 +80,17 @@
   DECLARE_VIRTUAL_TRACE();
 
  private:
+  using PromiseResolverFunction =
+      std::unique_ptr<Function<void(ScriptPromiseResolver*)>>;
+
   ImageCapture(ExecutionContext*, MediaStreamTrack*);
 
   void OnMojoGetPhotoState(ScriptPromiseResolver*,
+                           PromiseResolverFunction,
                            bool trigger_take_photo,
                            media::mojom::blink::PhotoStatePtr);
   void OnMojoSetOptions(ScriptPromiseResolver*,
+                        PromiseResolverFunction,
                         bool trigger_take_photo,
                         bool result);
   void OnMojoTakePhoto(ScriptPromiseResolver*, media::mojom::blink::BlobPtr);
@@ -92,6 +98,12 @@
   void UpdateMediaTrackCapabilities(media::mojom::blink::PhotoStatePtr);
   void OnServiceConnectionError();
 
+  void ResolveWithNothing(ScriptPromiseResolver*);
+  void ResolveWithPhotoSettings(ScriptPromiseResolver*);
+  void ResolveWithPhotoCapabilities(ScriptPromiseResolver*);
+  void ResolveWithMediaTrackConstraints(MediaTrackConstraints,
+                                        ScriptPromiseResolver*);
+
   Member<MediaStreamTrack> stream_track_;
   std::unique_ptr<WebImageCaptureFrameGrabber> frame_grabber_;
   media::mojom::blink::ImageCapturePtr service_;
@@ -99,12 +111,11 @@
   MediaTrackCapabilities capabilities_;
   MediaTrackSettings settings_;
   MediaTrackConstraintSet current_constraints_;
+  PhotoSettings photo_settings_;
 
   Member<PhotoCapabilities> photo_capabilities_;
 
-  HeapHashMap<Member<ScriptPromiseResolver>,
-              HeapVector<MediaTrackConstraintSet>>
-      service_requests_;
+  HeapHashSet<Member<ScriptPromiseResolver>> service_requests_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
index c9b84af..e819110 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
@@ -16,6 +16,7 @@
     [ImplementedAs=videoStreamTrack] readonly attribute MediaStreamTrack track;
 
     [CallWith=ScriptState] Promise<PhotoCapabilities> getPhotoCapabilities();
+    [CallWith=ScriptState] Promise<PhotoSettings> getPhotoSettings();
     [CallWith=ScriptState] Promise<void> setOptions(PhotoSettings photoSettings);
     [CallWith=ScriptState] Promise<Blob> takePhoto(optional PhotoSettings photoSettings);
     [CallWith=ScriptState] Promise<ImageBitmap> grabFrame();
diff --git a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
index c32e72d..882e38e1 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
+++ b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
@@ -13,13 +13,11 @@
 #include "modules/EventTargetModules.h"
 #include "modules/screen_orientation/LockOrientationCallback.h"
 #include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
+#include "platform/wtf/Assertions.h"
 #include "public/platform/modules/screen_orientation/WebScreenOrientationType.h"
 
 // This code assumes that WebScreenOrientationType values are included in
 // WebScreenOrientationLockType.
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
 STATIC_ASSERT_ENUM(blink::kWebScreenOrientationPortraitPrimary,
                    blink::kWebScreenOrientationLockPortraitPrimary);
 STATIC_ASSERT_ENUM(blink::kWebScreenOrientationPortraitSecondary,
diff --git a/third_party/WebKit/Source/platform/exported/WebString.cpp b/third_party/WebKit/Source/platform/exported/WebString.cpp
index 4ac5fbc..2147203 100644
--- a/third_party/WebKit/Source/platform/exported/WebString.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebString.cpp
@@ -31,6 +31,7 @@
 #include "public/platform/WebString.h"
 
 #include "base/strings/string_util.h"
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/text/ASCIIFastPath.h"
 #include "platform/wtf/text/AtomicString.h"
 #include "platform/wtf/text/CString.h"
@@ -38,9 +39,6 @@
 #include "platform/wtf/text/StringView.h"
 #include "platform/wtf/text/WTFString.h"
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enums: " #a)
 
 STATIC_ASSERT_ENUM(WTF::kLenientUTF8Conversion,
                    blink::WebString::UTF8ConversionMode::kLenient);
diff --git a/third_party/WebKit/Source/platform/loader/fetch/MemoryCacheTest.cpp b/third_party/WebKit/Source/platform/loader/fetch/MemoryCacheTest.cpp
index b525f9501..a263e1a 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/MemoryCacheTest.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/MemoryCacheTest.cpp
@@ -36,6 +36,7 @@
 #include "platform/loader/testing/MockResourceClient.h"
 #include "platform/testing/TestingPlatformSupport.h"
 #include "platform/testing/UnitTestHelpers.h"
+#include "platform/weborigin/KURL.h"
 #include "public/platform/Platform.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,8 +46,8 @@
  public:
   class FakeDecodedResource final : public Resource {
    public:
-    static FakeDecodedResource* Create(const ResourceRequest& request,
-                                       Type type) {
+    static FakeDecodedResource* Create(const String& url, Type type) {
+      ResourceRequest request(url);
       ResourceLoaderOptions options(kDoNotAllowStoredCredentials,
                                     kClientDidNotRequestCredentials);
       return new FakeDecodedResource(request, type, options);
@@ -199,28 +200,28 @@
 // Verified that when ordering a prune in a runLoop task, the prune
 // is deferred to the end of the task.
 TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_Basic) {
-  Resource* resource1 = FakeDecodedResource::Create(
-      ResourceRequest("http://test/resource1"), Resource::kRaw);
-  Resource* resource2 = FakeDecodedResource::Create(
-      ResourceRequest("http://test/resource2"), Resource::kRaw);
+  Resource* resource1 =
+      FakeDecodedResource::Create("http://test/resource1", Resource::kRaw);
+  Resource* resource2 =
+      FakeDecodedResource::Create("http://test/resource2", Resource::kRaw);
   TestResourcePruningAtEndOfTask(resource1, resource2);
 }
 
 TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_MultipleResourceMaps) {
   {
-    Resource* resource1 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource1"), Resource::kRaw);
-    Resource* resource2 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource2"), Resource::kRaw);
+    Resource* resource1 =
+        FakeDecodedResource::Create("http://test/resource1", Resource::kRaw);
+    Resource* resource2 =
+        FakeDecodedResource::Create("http://test/resource2", Resource::kRaw);
     resource1->SetCacheIdentifier("foo");
     TestResourcePruningAtEndOfTask(resource1, resource2);
     GetMemoryCache()->EvictResources();
   }
   {
-    Resource* resource1 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource1"), Resource::kRaw);
-    Resource* resource2 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource2"), Resource::kRaw);
+    Resource* resource1 =
+        FakeDecodedResource::Create("http://test/resource1", Resource::kRaw);
+    Resource* resource2 =
+        FakeDecodedResource::Create("http://test/resource2", Resource::kRaw);
     resource1->SetCacheIdentifier("foo");
     resource2->SetCacheIdentifier("bar");
     TestResourcePruningAtEndOfTask(resource1, resource2);
@@ -282,38 +283,38 @@
 }
 
 TEST_F(MemoryCacheTest, ClientRemoval_Basic) {
-  Resource* resource1 = FakeDecodedResource::Create(
-      ResourceRequest("http://foo.com"), Resource::kRaw);
-  Resource* resource2 = FakeDecodedResource::Create(
-      ResourceRequest("http://test/resource"), Resource::kRaw);
+  Resource* resource1 =
+      FakeDecodedResource::Create("http://foo.com", Resource::kRaw);
+  Resource* resource2 =
+      FakeDecodedResource::Create("http://test/resource", Resource::kRaw);
   TestClientRemoval(resource1, resource2);
 }
 
 TEST_F(MemoryCacheTest, ClientRemoval_MultipleResourceMaps) {
   {
-    Resource* resource1 = FakeDecodedResource::Create(
-        ResourceRequest("http://foo.com"), Resource::kRaw);
+    Resource* resource1 =
+        FakeDecodedResource::Create("http://foo.com", Resource::kRaw);
     resource1->SetCacheIdentifier("foo");
-    Resource* resource2 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource"), Resource::kRaw);
+    Resource* resource2 =
+        FakeDecodedResource::Create("http://test/resource", Resource::kRaw);
     TestClientRemoval(resource1, resource2);
     GetMemoryCache()->EvictResources();
   }
   {
-    Resource* resource1 = FakeDecodedResource::Create(
-        ResourceRequest("http://foo.com"), Resource::kRaw);
-    Resource* resource2 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource"), Resource::kRaw);
+    Resource* resource1 =
+        FakeDecodedResource::Create("http://foo.com", Resource::kRaw);
+    Resource* resource2 =
+        FakeDecodedResource::Create("http://test/resource", Resource::kRaw);
     resource2->SetCacheIdentifier("foo");
     TestClientRemoval(resource1, resource2);
     GetMemoryCache()->EvictResources();
   }
   {
-    Resource* resource1 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource"), Resource::kRaw);
+    Resource* resource1 =
+        FakeDecodedResource::Create("http://test/resource", Resource::kRaw);
     resource1->SetCacheIdentifier("foo");
-    Resource* resource2 = FakeDecodedResource::Create(
-        ResourceRequest("http://test/resource"), Resource::kRaw);
+    Resource* resource2 =
+        FakeDecodedResource::Create("http://test/resource", Resource::kRaw);
     resource2->SetCacheIdentifier("bar");
     TestClientRemoval(resource1, resource2);
     GetMemoryCache()->EvictResources();
diff --git a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
index 58d4071..d8569dc 100644
--- a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
+++ b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
@@ -10,6 +10,7 @@
 #include "media/base/mime_util.h"
 #include "media/filters/stream_parser_factory.h"
 #include "net/base/mime_util.h"
+#include "platform/wtf/Assertions.h"
 #include "platform/wtf/text/WTFString.h"
 #include "public/platform/FilePathConversion.h"
 #include "public/platform/InterfaceProvider.h"
@@ -53,9 +54,6 @@
   return ToLowerASCIIInternal(str.Characters16(), str.length());
 }
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enums: " #a)
 STATIC_ASSERT_ENUM(MIMETypeRegistry::kIsNotSupported, media::IsNotSupported);
 STATIC_ASSERT_ENUM(MIMETypeRegistry::kIsSupported, media::IsSupported);
 STATIC_ASSERT_ENUM(MIMETypeRegistry::kMayBeSupported, media::MayBeSupported);
diff --git a/third_party/WebKit/Source/platform/wtf/Assertions.h b/third_party/WebKit/Source/platform/wtf/Assertions.h
index e89a7cd..528d275 100644
--- a/third_party/WebKit/Source/platform/wtf/Assertions.h
+++ b/third_party/WebKit/Source/platform/wtf/Assertions.h
@@ -211,4 +211,9 @@
   void To##thisType##OrDie(const thisType*);                                  \
   void To##thisType##OrDie(const thisType&)
 
+// Check at compile time that related enums stay in sync.
+#define STATIC_ASSERT_ENUM(a, b)                            \
+  static_assert(static_cast<int>(a) == static_cast<int>(b), \
+                "mismatching enum: " #a)
+
 #endif  // WTF_Assertions_h
diff --git a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
index 5fba494..197cf5f 100644
--- a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
+++ b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
@@ -133,10 +133,6 @@
 
 namespace blink {
 
-#define STATIC_ASSERT_ENUM(a, b)                            \
-  static_assert(static_cast<int>(a) == static_cast<int>(b), \
-                "mismatching enum: " #a)
-
 STATIC_ASSERT_ENUM(WebApplicationCacheHost::kUncached,
                    ApplicationCacheHost::kUncached);
 STATIC_ASSERT_ENUM(WebApplicationCacheHost::kIdle, ApplicationCacheHost::kIdle);
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index 02f43dc0..4d6fb9917 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -82,27 +82,6 @@
   }
 }
 
-static_library("test_support") {
-  deps = [
-    "//skia",
-    "//third_party/WebKit/Source/core:testing",
-    "//third_party/WebKit/Source/modules:modules_testing",
-    "//third_party/WebKit/Source/platform/wtf",
-    "//v8",
-  ]
-
-  sources = [
-    "WebTestingSupport.cpp",
-  ]
-
-  configs += [
-    "//third_party/WebKit/Source:config",
-    "//third_party/WebKit/Source/core:blink_core_pch",
-  ]
-
-  include_dirs = [ "$root_gen_dir/blink" ]
-}
-
 group("webkit_unit_tests_data") {
   data = [
     "tests/data/",
@@ -116,10 +95,8 @@
 
   sources = [
     "PageOverlayTest.cpp",
-    "WebAssociatedURLLoaderImplTest.cpp",
     "WebDragDataTest.cpp",
     "WebElementTest.cpp",
-    "WebNodeTest.cpp",
 
     # FIXME: Move the tests from web/tests/ to appropriate places.
     # crbug.com/353585
@@ -193,7 +170,6 @@
   sources += bindings_unittest_files
 
   deps = [
-    ":test_support",
     ":web",
     "//base",
     "//base:i18n",
@@ -205,6 +181,7 @@
     "//testing/gtest",
     "//third_party/WebKit/Source/core:unit_tests",
     "//third_party/WebKit/Source/modules:unit_tests",
+    "//third_party/WebKit/Source/modules/exported:test_support",
     "//third_party/WebKit/Source/platform:test_support",
     "//third_party/WebKit/Source/platform:unit_tests",
     "//third_party/WebKit/Source/platform/wtf",
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 4fbfdced..cd29bd6 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -29,8 +29,8 @@
   testonly = true
   public_deps = [
     ":test_headers",
+    "//third_party/WebKit/Source/modules/exported:test_support",
     "//third_party/WebKit/Source/platform:test_support",
-    "//third_party/WebKit/Source/web:test_support",
   ]
 }
 
diff --git a/third_party/WebKit/public/platform/WebMediaConstraints.h b/third_party/WebKit/public/platform/WebMediaConstraints.h
index 7d8cbf21..78692872 100644
--- a/third_party/WebKit/public/platform/WebMediaConstraints.h
+++ b/third_party/WebKit/public/platform/WebMediaConstraints.h
@@ -31,14 +31,15 @@
 #ifndef WebMediaConstraints_h
 #define WebMediaConstraints_h
 
+#include <string>
+#include <vector>
+
 #include "WebCommon.h"
 #include "WebNonCopyable.h"
 #include "WebPrivatePtr.h"
 #include "WebString.h"
 #include "WebVector.h"
 
-#include <vector>
-
 namespace blink {
 
 class WebMediaConstraintsPrivate;
@@ -167,6 +168,8 @@
 
   void SetExact(const WebVector<WebString>& exact) { exact_.Assign(exact); }
 
+  void SetIdeal(const WebString& ideal) { ideal_.Assign(&ideal, 1); }
+
   void SetIdeal(const WebVector<WebString>& ideal) { ideal_.Assign(ideal); }
 
   bool Matches(WebString value) const;
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bd9f8b1d..608595b4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22434,6 +22434,7 @@
   <int value="-506706655" label="respect-autocomplete-off-autofill"/>
   <int value="-505679399" label="FontCacheScaling:enabled"/>
   <int value="-495585885" label="enable-spdy-proxy-dev-auth-origin"/>
+  <int value="-494722408" label="ContentSuggestionsLargeThumbnail:enabled"/>
   <int value="-493551777" label="StaleWhileRevalidate2:disabled"/>
   <int value="-492864618" label="NTPForeignSessionsSuggestions:disabled"/>
   <int value="-488779992" label="blink-settings"/>
@@ -22552,6 +22553,7 @@
   <int value="-70595606" label="ash-enable-unified-desktop"/>
   <int value="-68877684" label="BackgroundVideoTrackOptimization:enabled"/>
   <int value="-68225452" label="enable-translate-new-ux"/>
+  <int value="-59401847" label="ContentSuggestionsLargeThumbnail:disabled"/>
   <int value="-58242474" label="ash-disable-swipe-to-close-in-overview-mode"/>
   <int value="-57986995" label="DisablePostScriptPrinting:enabled"/>
   <int value="-55944747" label="disable-child-account-detection"/>